Skip to content

Errors

Every message the packages throw, verbatim, grouped by error class. Registration errors fire where the provider is registered, not where it is later read; resolution errors fire at the read. X and Y stand for your token names.

The error classes

@remodulo/container throws four typed classes, all exported. Catch by instanceof, or by code - the code survives two copies of the package in one process, where instanceof does not:

ClasscodeCarries
RegistrationErrorREMODULO/REGISTRATIONtoken - when the failing provider names one
ResolutionErrorREMODULO/RESOLUTIONtoken, mode - the width of the failed read
CycleError (extends ResolutionError)REMODULO/CYCLEchain - the ring as an array of tokens
InjectionContextErrorREMODULO/INJECTION_CONTEXTcaller - which injector was misused
ts
try {
    container.resolve(OrdersApi)
} catch (error) {
    if (error instanceof ResolutionError) report(error.code, error.token, error.mode)
}

@remodulo/react throws plain Errors - and two AggregateErrors in teardown. No codes; match by message.

RegistrationError - REMODULO/REGISTRATION

Fires at register, where the provider is declared.

Token X is already registered on this container

Token X is already registered on this container. One token, one registration - mark every provider for it multi: true to make it a collection, or give each provider its own token.

When: a second single registration claimed an already-claimed token on the same container. Fix: as the message says - multi: true everywhere, or separate tokens.

Token X is already single / multi

Token X is already a single registration on this container, and this provider registers it as a multi-provider collection. A token is one or the other for the whole container chain - that is what lets resolve and resolveAll agree about what it means. Drop multi: true here, or add it to the other registration.

When: a provider disagrees with the token's existing mode. "On an ancestor container" means the conflict crosses the chain; the fix sentence flips direction with the mismatch. Fix: make every provider for the token agree on multi.

Provider with multi: true requires provide

Provider with multi: true requires provide - the class shorthand registers under the class itself, and a collection whose only member is that class is just the class. Name the collection's token explicitly.

When: { useClass, multi: true } with no provide. Fix: give the collection an explicit token.

Provider with useValue / useFactory / useExisting requires provide

Provider with useFactory requires provide - only useClass may register under its own token, because a class is one. Give this provider an explicit token.

When: an object form other than { useClass } has no provide. Fix: add the token.

X has no recognised form

X has no recognised form - expected a class, or an object with one of useClass, useValue, useFactory or useExisting.

When: register received something that is neither a class nor a valid provider object. Fix: check the value - a typo'd key, an accidental instance, an undefined import.

X mixes implementation keys

X mixes 2 implementation keys (useClass, useValue) - a provider declares exactly one of useClass, useValue, useFactory or useExisting. Note that an explicit undefined still counts as declared.

When: a provider object carries more than one use* key - including one set to undefined. Fix: keep exactly one.

Provider for X cannot alias Y

Provider for X cannot alias Y: Y is a multi-provider collection, and useExisting is a single-value read of its target - it redirects to exactly the read resolve performs, and resolve refuses a collection. Alias a single registration, or contribute to the collection with { provide: Y, ..., multi: true }.

When: useExisting targets a collection token - in either registration order. Fix: as the message says.

ResolutionError - REMODULO/RESOLUTION

Fires at the read.

Token X is not registered

Token X is not registered in this container or any ancestor.

When: resolve / inject found no declaration at this container or above. Fix: register the token here or in an ancestor, or read with resolveOptional / resolveOr if absence is legal.

Token X is not registered in this container (mode "self" reads its own bindings only). Use "nearest" to search its ancestors too.

When: the same miss under mode: "self" - an inherited registration is a miss by design. Fix: use "nearest", or declare the token on this container.

Token X is a multi-provider collection

Token X is a multi-provider collection - several providers contribute to it, so there is no single value to read. Use resolveAll.

When: resolve / inject / entry on a collection token. Fix: resolveAll / injectAll / entries.

Token X is a single registration

Token X is a single registration, not a multi-provider collection - resolveAll would hide that behind a one-element array. Use resolve, or mark every provider for it multi: true.

When: resolveAll / injectAll / entries on a single-registration token. Fix: resolve, or make the token a collection.

CycleError - REMODULO/CYCLE

Circular dependency found

Circular dependency found: A -> B -> A

When: two providers need each other at construction; the message is the ring, alias hops included, and the same ring is on error.chain. Fix: { delayed: true } on one side - Circular Dependencies.

InjectionContextError - REMODULO/INJECTION_CONTEXT

inject() was called outside a construction frame

inject(X) was called outside a construction frame. inject reads the container that is building right now, so it only works synchronously inside a constructor body, a field initializer, or a useFactory body. In an async factory that means BEFORE the first await - once the factory yields, the frame is gone. Outside construction, read from a container directly with resolve, or open a frame explicitly with runInInjectionContext. A frame that should be there and is not can also mean two copies of @remodulo/container in one process: each holds its own frame, and injection never crosses between them.

When: an injector ran outside construction - a method, a callback, after an await. error.caller names the injector. Fix: move the read into construction, take injectResolver() at construction and read later, or use { delayed: true }. See Dependency Injection.

makeTokenizer

Plain errors, thrown at mint time.

makeTokenizer: namespace must be a non-empty string.

Token: name must be a non-empty string.

When: an empty or whitespace-only namespace or token name. Fix: pass real names; the namespace should be your package name.

@remodulo/react

Plain Errors - no codes, match by message.

ModuleProvider requires a parent module

ModuleProvider requires a parent module in context. Wrap it in <AppProvider>, or nest it under another <ModuleProvider>.

When: a scoped module rendered outside <AppProvider> - including a createModuleComponent component. Fix: as the message says. See The Tree.

useModuleContext: no module in context

useModuleContext: no module in context. Wrap with <AppProvider> or <ModuleProvider>.

When: useModuleContext / useModule / useResolver / useResolve outside any module boundary. Fix: move the component under a module, or wrap it in one.

AppProvider does not support replacing its App instance

AppProvider does not support replacing its App instance

When: the app prop changed identity between renders - typically a module-level const app in a file that hot reloads, or new App(...) inline in JSX. Fix: keep one instance for the life of the tree - export the const, or pass a factory: app={() => new App({ ... })}.

App failed to initialize.

App failed to initialize.

When: a provider threw during the App's init; the original error is logged right before this one. Fix: read the logged error - this one only reports the state.

App was destroyed. Provide a fresh App.

App was destroyed. Provide a fresh App.

When: an AppProvider received an App whose lifecycle already ended. Fix: construct a new App - a destroyed module is not revivable.

Cannot create a child module from an un-initialized parent

Cannot create a child module from an un-initialized parent - its lifecycle is not armed yet, so instances would leak. Init the parent first.

When: a module was constructed under a parent that has not run init() yet. Fix: init the parent first; under React this ordering is handled for you.

Cannot create a child module under a failed / destroyed parent

Cannot create a child module under a failed parent - that branch is spent, so the child could never be armed. Build it under a live parent, or rebuild the branch first.

When: constructing a module under a parent that is failed, destroying, or destroyed. Fix: rebuild the branch - Module Rebuild.

Cannot mount a module onto a failed / destroying / destroyed parent

Cannot mount a module onto a failed parent - that branch is spent, so the child could never go live under it. Mount it under a live parent, or rebuild the branch first.

When: the boundary mounted while its parent module was already dying. Fix: rebuild the branch, or fix whatever failed the parent - the log right above has it.

Cannot init() / mount() / unmount() / destroy() a module whose status is X

Cannot mount() a module whose status is "created" - mount() accepts "initialized" or "unmounted".

When: a lifecycle signal arrived in the wrong phase. Under React this indicates a bug above the module layer - the providers drive these calls in order. Fix: if you drive a module by hand, follow Lifecycle Phases.

Cannot resolve X from a module whose status is "..."

Cannot resolve X from a module whose status is "failed" - a module answers reads only once init() has armed it, and never after it failed or was destroyed.

When: a read through a module before init, or after it failed or was destroyed. Fix: for a failed module, the init error above it is the real problem; a destroyed module needs a rebuild.

Cannot resolve X from an unhealthy module tree

Cannot resolve X from an unhealthy module tree - failed branch: orders. Every module under a failed ancestor refuses reads, whichever module owns the binding.

When: a read anywhere below a failed or destroyed ancestor; the id in the message is the ancestor's. Fix: fix or rebuild the named branch.

Provider for X declares lazy: ... while the collection is lazy: ...

Provider for X declares lazy: true while the collection already registered for that token is lazy: false. A collection is constructed whole - one resolveAll in the owner's eager pass - so a partly-lazy one has no coherent meaning. Make every useClass, useFactory, useValue and useExisting member agree.

When: a collection's members disagree on lazy. Fix: all eager or all lazy.

AggregateError: Errors occurred while unmounting module subtree

When: one or more onModuleUnmount hooks threw; every participant still ran, and the errors are collected on error.errors. Fix: each entry is a real bug in a hook - unmount itself completed.

AggregateError: Module mount failed and rollback encountered errors

When: onModuleMount threw AND the best-effort rollback hit further errors; the first entry is the original mount error, the rest are teardown fallout. Fix: the first entry is the cause.

MIT licensed.