Runtime Exception Hierarchy¶
Trussium defines a stable exception hierarchy for failures intentionally created and classified by the runtime. It gives applications and future runtime services useful catch boundaries without converting arbitrary Python, framework, or provider SDK exceptions into public Trussium contracts.
Hierarchy¶
RuntimeError
└── TrussiumError
├── ConfigurationError
│ ├── RuntimeServiceRegistryError
│ │ ├── RuntimeServiceAlreadyRegisteredError
│ │ ├── RuntimeServiceNotFoundError
│ │ └── RuntimeServiceRegistrySealedError
│ └── CapabilityRegistryError
│ ├── CapabilityAlreadyRegisteredError
│ ├── CapabilityNotFoundError
│ ├── CapabilityRegistrySealedError
│ └── CapabilityContractMismatchError
└── RuntimeExecutionError
├── LifecycleError
│ ├── RuntimeServiceLifecycleError
│ ├── RuntimeServiceStateError
│ ├── CapabilityLifecycleError
│ └── CapabilityLifecycleStateError
├── DependencyError
└── CapabilityError
├── CapabilityExecutionError
└── ProviderError
└── OpenAIProviderError
The public domain bases are available from both trussium and
trussium.errors:
from trussium import ProviderError, TrussiumError
try:
await run_application()
except ProviderError as error:
handle_provider_failure(error.code)
except TrussiumError as error:
handle_runtime_failure(error.code)
Concrete compatibility types retain their established modules:
from trussium.capabilities.errors import CapabilityExecutionError
from trussium.providers.openai import OpenAIProviderError
Concrete runtime-service lifecycle types are exported from trussium.runtime:
Runtime-service registry types are also exported from trussium.runtime:
Capability-registry types are exported from trussium.capabilities:
Capability-lifecycle failures are exported from trussium.capabilities:
Public attributes¶
Every TrussiumError has:
code: a non-empty stable machine-readable identifier.message: a non-empty client-safe description.str(error): the same client-safe description.
Domain bases supply stable defaults:
| Error | Default code |
|---|---|
TrussiumError |
trussium_error |
ConfigurationError |
configuration_error |
RuntimeExecutionError |
runtime_execution_error |
LifecycleError |
lifecycle_error |
DependencyError |
dependency_error |
CapabilityError |
capability_error |
ProviderError |
provider_error |
Concrete errors should provide a more specific stable code when callers need to distinguish outcomes. Codes are lowercase snake case, describe a durable condition rather than an implementation, and must not contain user or provider data.
CapabilityExecutionError continues to add its protocol-neutral
CapabilityErrorCategory. Existing HTTP status mapping, JSON details, SSE
events, provider codes, and safe messages are unchanged.
HTTP error envelope¶
JSON API failures use a stable detail object with code and message:
Request validation returns HTTP 422 with the validation_error code and only
bounded field paths. It never echoes rejected values, raw Pydantic messages,
credentials, or request payloads. Runtime-owned failures retain their existing
codes and messages in the same detail shape; fields is omitted when it is
not applicable.
CapabilityExecutionPipeline adds no exception type or translation. Missing
registrations retain CapabilityNotFoundError; normalized execution errors,
native cancellation, generator exit, and unexpected callback failures
propagate unchanged. The chat transport retains its existing missing-capability
and category-to-HTTP mappings.
Capability middleware also adds no normalized error category. Middleware
results and failures propagate unchanged. A layer that calls its continuation
more than once receives a deterministic RuntimeError before duplicate
downstream execution. Streaming cleanup attempts every created layer and does
not replace an already active execution failure with a later cleanup failure.
Catch boundaries¶
Catch the narrowest type that supports the required recovery:
ConfigurationErrorfor normalized configuration failures owned by Trussium. PydanticValidationErrorremains Pydantic-owned at settings and process-startup boundaries. Runtime-service and capability-registry duplicate, required-lookup, and sealed failures inherit this branch; known capability contract mismatches do too. Invalid registry names andNonecapability implementations remainValueError.LifecycleErrorfor normalized runtime startup, drain, or shutdown failures. Runtime-service and capability hooks normalize startup and cleanup failures into bounded aggregate metadata after all eligible hooks run. Existing injected readiness and tracing cleanup exceptions retain their established behavior.DependencyErrorfor normalized external dependency operations. Bounded readiness results remain value objects and do not become exceptions.CapabilityExecutionErrorwhen category-aware request handling is needed.ProviderErrorfor safe provider-adapter failures such as response normalization.TrussiumErroronly when one policy legitimately handles every owned runtime failure.
Do not catch TrussiumError as a substitute for handling programmer defects
or arbitrary third-party failures.
Exceptions outside the hierarchy¶
The following retain their native identities and semantics:
asyncio.CancelledErrorandGeneratorExit.- Starlette client disconnects and FastAPI
HTTPException. - Pydantic validation failures.
- OpenAI and other provider SDK exceptions before an adapter normalizes them.
- Built-in
TimeoutErrorbefore a runtime deadline boundary normalizes it. - Unexpected programming errors outside the explicit runtime-service lifecycle boundary.
In particular, cancellation must continue to propagate so async tasks and stream generators terminate correctly.
Privacy and observability¶
Codes and messages may appear in client envelopes, logs, metrics, or traces. They must never include:
- Credentials, authorization headers, or tokens.
- Provider or collector endpoints.
- Prompts, completions, request bodies, or provider responses.
- Request or execution identifiers inside the error code.
- Raw exception text from infrastructure or provider SDKs.
Log the stable code and bounded domain type. Unexpected exceptions may be recorded through the existing internal exception path, but operational events must continue to exclude their message and traceback.
Extending the hierarchy¶
New runtime-owned errors should:
- Inherit from the narrowest existing domain base.
- Use a stable default or concrete code.
- Expose only a client-safe message.
- Preserve original exceptions with
raise ... from errorwhere useful. - Leave cancellation and framework control flow untouched.
- Add inheritance, public-attribute, boundary, and envelope tests.
Adding a new branch or changing inheritance is a public API change. Renaming a code or changing a client message or transport mapping requires explicit compatibility review.