Errors and exceptions¶
Every exception modern-di raises lives in modern_di.exceptions and descends from a single
root, ModernDIError. The hierarchy is grouped by when the failure happens — registering
providers, validating the graph, resolving a type, or closing a container — so you can catch a
whole category with one except.
Hierarchy¶
ModernDIError (RuntimeError)
├── ContainerError
│ ├── InvalidChildScopeError
│ ├── MaxScopeReachedError
│ ├── ScopeNotInitializedError
│ ├── ScopeSkippedError
│ ├── InvalidScopeTypeError
│ ├── ContainerClosedError
│ └── ValidationFailedError
├── ResolutionError
│ ├── ProviderNotRegisteredError
│ ├── AliasSourceNotRegisteredError
│ ├── ArgumentResolutionError
│ ├── CircularDependencyError
│ ├── CreatorCallError
│ └── ContextValueNotSetError
├── RegistrationError
│ ├── DuplicateProviderTypeError
│ ├── ChildContainerRegistrationError
│ ├── GroupScopeConflictError
│ ├── ProviderScopeFrozenError
│ ├── UnknownFactoryKwargError
│ ├── UnsupportedCreatorParameterError
│ └── InvalidScopeDependencyError
├── FinalizerError
├── AsyncFinalizerInSyncCloseError
└── GroupInstantiationError
Root¶
ModernDIError— base class for every error the library raises. It subclassesRuntimeErrorfor backwards compatibility, soexcept RuntimeErrorkeeps working. CatchModernDIErrorto handle any framework error in one place.
ContainerError — container and scope problems¶
Catch ContainerError for any container/scope failure.
InvalidChildScopeError— raised whenbuild_child_container(scope=...)is given a scope that is not deeper than the parent's (or the constructor receives a parent at an equal/shallower scope). The error lists the scopes that are allowed. See Troubleshooting: InvalidChildScopeError.MaxScopeReachedError— raised bybuild_child_container()with no explicitscopewhen the parent is already at the deepest scope (STEP), so there is no next level to advance to. See Troubleshooting: MaxScopeReachedError.ScopeNotInitializedError— raised during resolution when a provider needs a scope deeper than the current container's, and no container at that scope exists in the chain (e.g. resolving aREQUEST-scoped provider from theAPPcontainer). LikeResolutionError, it carries a breadcrumbdependency_path: a runtime captive dependency (a shallower-scoped provider depending, directly or transitively, on this deeper-scoped one) names both the capturing provider and the one that actually failed, not just the two scope names. See Troubleshooting: ScopeNotInitializedError.ScopeSkippedError— raised during resolution when the target scope is shallower than the current container but is missing from the scope chain (a level was skipped when building children). Carries the same breadcrumbdependency_pathasScopeNotInitializedError. See Troubleshooting: ScopeSkippedError.InvalidScopeTypeError— raised by theContainerconstructor whenscopeis not anenum.IntEnum. See Troubleshooting: InvalidScopeTypeError.ContainerClosedError— no longer raised as of modern-di 3.1; kept importable for back-compat and removed in 4.0. A container is open from construction, so there is nothing to raise: resolving from a container that was explicitly closed — directly, or through a child whose resolve reaches back into its scope — reopens it and emitsContainerClosedWarning(aRuntimeWarning, not aModernDIError) instead.build_child_container()itself never checks or touches any container's open/closed state — building a child of a closed parent triggers neither the reopen nor the warning by itself. Re-enter the container viawith/async with, or callcontainer.open(), to reopen it deliberately (silently) instead — see Lifecycle: closing and reopening. See Troubleshooting: ContainerClosedError.ValidationFailedError— raised only byContainer.validate(). Catch this for validation results; its.errorsattribute holds the list of individual issues (each itself aResolutionErrororRegistrationError), andstr()renders them all, grouped by error kind. Nothing validates automatically — not construction, notopen(), notadd_providers, notresolve()— so callvalidate()explicitly whenever you want the whole graph checked; an integration that registers its own providers after construction (viaadd_providers) should call it after that registration.Container(validate=...)is a deprecated no-op: passingTrueorFalseemitsValidateArgumentWarningand gates nothing. See Lifecycle: validation, Migration: To 3.x and Troubleshooting: ValidationFailedError.
ResolutionError — failures while resolving a type¶
Catch ResolutionError for any resolution failure. These carry a dependency_path that is
accumulated as the error propagates, so the message shows the full chain from the requested type
down to the failing dependency. dependency_path is a list[ResolutionStep], where each
ResolutionStep (importable from modern_di.exceptions) has a .scope and a .name — inspect it
to render the chain programmatically. ScopeNotInitializedError and ScopeSkippedError (below) carry
the same dependency_path — the breadcrumb machinery is shared, not duplicated.
ProviderNotRegisteredError— raised byresolve(SomeType)when no provider is registered for the type. The message includes "did you mean…" suggestions when a close match exists. See Troubleshooting: Missing provider.AliasSourceNotRegisteredError— raised when anAliaspoints at asource_typethat has no registered provider (eagerly duringvalidate(), or at resolution time). See Troubleshooting: AliasSourceNotRegisteredError.ArgumentResolutionError— raised when a creator parameter cannot be resolved: no provider matches its annotated type, or the parameter is unannotated. See Troubleshooting: ArgumentResolutionError.CircularDependencyError— raised when the provider graph contains a cycle (A → B → A); the message shows the cycle path. Raised eagerly byvalidate(), and also by a bareresolve()on an unvalidated cyclic graph via a runtime guard — see Troubleshooting: Circular dependency.CreatorCallError— raised when a creator's dependencies all resolved but argument binding failed while calling it (the assembled arguments don't match the signature — typically akwargs/skip_creator_parsingmismatch). Exceptions raised inside the creator body propagate unchanged, never wrapped. The bindingTypeErroris preserved on.original_error(and as the__cause__). See Troubleshooting: CreatorCallError.ContextValueNotSetError— raised when an unsetContextProvideris resolved directly (container.resolve(SomeContextType)with no value set); there is no fallback. See Migration: To 3.x. Only the direct-resolve path is affected — aFactoryparameter backed by the sameContextProviderkeeps following its own default/nullable/required disposition. Inspect.context_type. See Troubleshooting: Context not set.
RegistrationError — declaration / registration problems¶
Catch RegistrationError for declaration- and registration-time problems.
DuplicateProviderTypeError— raised when two providers are registered for the same bound type (within one group, across groups passed together, or against an already-registered type). See Troubleshooting: Duplicate type.ChildContainerRegistrationError— raised byContainer.add_providers()when called on a child container; registration is root-only because the providers registry is shared tree-wide, so registering from a child would mutate every container in the tree. Calladd_providerson the root container instead. Inspect.scopefor the offending child container's scope. See Container: registering after construction and Troubleshooting: ChildContainerRegistrationError.GroupScopeConflictError— raised when a scope-defaulted provider (no explicitscope=) is shared by twoGroupsubclasses declared with differentscope=kwargs; the provider's scope cannot follow both defaults at once, and import order must never be what decides it. Inspect.provider_name,.first_group/.first_scope, and.second_group/.second_scope. See Troubleshooting: GroupScopeConflictError.ProviderScopeFrozenError— raised when aGroupwould change the scope of a provider that is already registered with a container. Resolvers compiled before the change captured the old scope, so applying it would make the same provider resolve differently through an existing container than through a fresh one. Inspect.provider_name,.group_name,.current_scope,.new_scope. See Troubleshooting: ProviderScopeFrozenError.UnknownFactoryKwargError— raised whenFactory(kwargs={...})contains a key that is not a parameter of the creator's signature; lists the known parameters and "did you mean" hints. See Troubleshooting: UnknownFactoryKwargError.UnsupportedCreatorParameterError— raised when a creator's signature has a parametermodern-dicannot wire (e.g. an unsupported kind); names the parameter and the reason. See Troubleshooting: UnsupportedCreatorParameterError.InvalidScopeDependencyError— raised when a provider depends on another provider bound to a deeper scope than its own (a longer-lived provider depending on a shorter-lived one). Surfaced byvalidate(). See Troubleshooting: Scope chain.
Direct ModernDIError subclasses¶
These don't fit the register/resolve/validate grouping:
FinalizerError— raised byclose_sync()/close_async()when one or more finalizers raised during cleanup. The remaining finalizers still run; all errors are aggregated into this single exception..finalizer_errorsholds the list and.is_asyncrecords which close path ran. See Lifecycle and Troubleshooting: FinalizerError.AsyncFinalizerInSyncCloseError— raised whenclose_sync()reaches a cached resource whose finalizer is async. Becauseclose_sync()aggregates, this arrives wrapped inside aFinalizerError(as an entry in.finalizer_errors), not on its own. The cache is retained so a laterawait close_async()can finalize it. See Lifecycle and Troubleshooting: AsyncFinalizerInSyncCloseError.GroupInstantiationError— raised when aGroupsubclass is instantiated. Groups are namespaces and must never be created as objects. See Troubleshooting: GroupInstantiationError.
Security note¶
modern-di exception messages are intended for developers (logs, tracebacks during wiring). A
CreatorCallError embeds the wrapped exception's text, and a FinalizerError embeds the repr of every
finalizer exception — so if a creator or finalizer raises an error whose message contains sensitive
runtime data, that text becomes part of the modern-di message. The DI-specific errors themselves are
conservative (type names and provider reprs only; context values are keyed by type and never repr'd).
Applications must not echo raw exception strings to untrusted clients.