Skip to content

Errors

InferDI throws explicit errors for graph and lifecycle misuse. Keep these messages visible in tests so registration mistakes fail early.

TriggerMessage shape
.get(k) on missing keyKey "k" not found
Disposed container resolveContainer is disposed (key: "k")
Disposed ancestor resolveAncestor container is disposed (key: "k")
createScope() after disposeCannot create scope from a disposed container
Registration after disposeCannot register on a disposed container (key: "k")
Root resolves a scoped key with fast: falseScoped "k" cannot be resolved from the root container. Use createScope().
Singleton lifetime violationSingleton "x" cannot depend on scoped "y"...
Synchronous cycleCircular dependency detected: a -> b -> a...
Sync dispose over async resourceSync [Symbol.dispose] called on a resource whose .dispose() returned a Promise...
Sync dispose over cached async initializationSync [Symbol.dispose] called on a container that cached a Promise from an async factory...
Late overrideCannot override "k" because it has already been resolved...
Override on disposed containerCannot override on a disposed container (key: "k")

Sync disposal observes the rejection of a cached native Promise before reporting the misuse. A rejection that arrives later does not become an unhandledRejection, but sync disposal still cannot await or close the resource. It does not call .then() on a custom Promise-like value.

During async disposal, a failed dependency and its dependent registrations may reject with the same Error object. InferDI reports that object once. Separate error objects remain separate causes in AggregateError, even when their messages match.

Async Factory Cycles

Declarative edges listed in registerAsyncFactory(..., deps, ...) are resolved by a synchronous preflight, so the existing cycle guard rejects cycles before any factory body starts.

Cycles created after a Promise boundary are not detected. This includes calls from Promise-valued registerFactory callbacks and captured containers used after await. If both sides wait for each other, callers observe a pending Promise that never resolves.

Fix async cycles architecturally:

  • split shared initialization
  • hoist one side into an earlier service
  • use Lazy<singleton> only for synchronous singleton edges
  • add a development watchdog timeout around suspicious top-level awaits

Adapter Cleanup Errors

Adapter cleanup errors after a response is produced are never surfaced to the client. They are routed to onDisposeError or the adapter's fallback sink.

Setup failures are different: the original setup error is surfaced, and any cleanup failure during setup teardown is routed to the sink without being aggregated into the surfaced error.