Skip to Content
ErrorsNEXUS_NOT_READY

This page documents the 0.4 release candidates of @nexusdi/core. The newest on npm is 0.4.0-rc.1. The documentation for 0.3.2, the current release, is at nexus.js.org. Try the release candidate.

NEXUS_NOT_READY

NEXUS_NOT_READY means a function from a lazy() dependency ran before its target was ready, such as from the constructor of the class that holds it. At startup the construction fails, so Nexus.create rejects with NEXUS_PROVIDER_FAILED and this error is its cause. After startup, a get() of an eager: false provider throws it directly when a thunk reaches a provider that is still building.

Reproduce NEXUS_NOT_READY

The region calls the lazy(SHIELD_GRID) thunk inside the PlasmaRouter constructor, while ShieldGrid waits for the router. Core formats the cause when it raises it. Without a plugin, the cause carries a one-line message that starts with the code and ends with the address of this page. With errors() from @nexusdi/errors registered (npm install @nexusdi/errors@next), the cause carries the full text and its fix.

import { Nexus, Token, defineModule, isNexusError, lazy } from '@nexusdi/core'; import { provide, type NexusPlugin } from '@nexusdi/core'; import { errors } from '@nexusdi/errors'; interface IPowerRouter { divert(): number; } interface IShieldGrid { draw(): number; } const POWER_ROUTER = new Token<IPowerRouter>('PowerRouter'); const SHIELD_GRID = new Token<IShieldGrid>('ShieldGrid'); class PlasmaRouter implements IPowerRouter { static deps = [lazy(SHIELD_GRID)] as const; private readonly level: number; constructor(shields: () => IShieldGrid) { // The thunk runs inside the constructor, before ShieldGrid exists. this.level = shields().draw(); } divert() { return this.level; } } class DeflectorGrid implements IShieldGrid { static deps = [POWER_ROUTER] as const; constructor(readonly router: IPowerRouter) {} draw() { return 0.4; } } const Engineering = defineModule({ name: 'Engineering', providers: [ provide(POWER_ROUTER, { useClass: PlasmaRouter }), provide(SHIELD_GRID, { useClass: DeflectorGrid }), ], }); const startup = (plugins: NexusPlugin[]) => Nexus.create(Engineering, { plugins }).catch((error: unknown) => error); const thin = await startup([]); if (!isNexusError(thin, 'NEXUS_PROVIDER_FAILED')) throw thin; const line = isNexusError(thin.cause) ? thin.cause.message : null; // -> '[NEXUS_NOT_READY] owner=PowerRouter target=ShieldGrid. https://nexus.js.org/errors/NEXUS_NOT_READY' console.log(line); const full = await startup([errors()]); if (!isNexusError(full, 'NEXUS_PROVIDER_FAILED')) throw full; const text = isNexusError(full.cause) ? full.cause.message.split('\n') : null; // -> ['[NEXUS_NOT_READY] PowerRouter called its lazy(ShieldGrid) thunk before ShieldGrid was ready.', " Fix: call the thunk after startup, from a method, and not from a constructor, a factory's continuation or onInit."] console.log(text);

Try it: save the region as error.ts and run npx tsx error.ts. It prints the one-line message, then the full text as an array of two strings. The owner and target fields name the provider that called the thunk and the one it asked for. Then save the fix region below as fixed.ts and run npx tsx fixed.ts. It prints 0.4.

Fix NEXUS_NOT_READY

NEXUS_NOT_READY is fixed by calling the thunk from a method that runs after startup. A constructor runs too early, because the container has not built the target yet.

import { Nexus, Token, defineModule, lazy, provide } from '@nexusdi/core'; interface IPowerRouter { divert(): number; } interface IShieldGrid { draw(): number; } const POWER_ROUTER = new Token<IPowerRouter>('PowerRouter'); const SHIELD_GRID = new Token<IShieldGrid>('ShieldGrid'); class PlasmaRouter implements IPowerRouter { static deps = [lazy(SHIELD_GRID)] as const; constructor(private readonly shields: () => IShieldGrid) {} divert() { return this.shields().draw(); } } class DeflectorGrid implements IShieldGrid { static deps = [POWER_ROUTER] as const; constructor(readonly router: IPowerRouter) {} draw() { return 0.4; } } await using ship = await Nexus.create( defineModule({ name: 'Engineering', providers: [ provide(POWER_ROUTER, { useClass: PlasmaRouter }), provide(SHIELD_GRID, { useClass: DeflectorGrid }), ], }), ); const diverted = ship.get(POWER_ROUTER).divert(); // -> 0.4 console.log(diverted);

Try it: in fixed.ts, change the PlasmaRouter constructor to constructor(private readonly shields: () => IShieldGrid) { shields(); } and run it again. Nexus.create rejects with a ProviderError for PowerRouter in Engineering, and its cause is the NotReadyError from the reproduce region. The uncaught error starts with [NEXUS_PROVIDER_FAILED] token=PowerRouter module=Engineering path=PowerRouter.. Remove the call from the constructor and the program prints 0.4 again.

Fields of NotReadyError

NotReadyError carries these fields:

  • owner: the provider whose thunk ran.
  • target: the provider the thunk resolves.
  • path: empty unless the target is in the middle of its own build. Then it lists the providers being built, from the first frame of the target to the provider that called the thunk, and ends with the target’s name. The rule is the same at startup and at get(). At startup the error sits inside a ProviderError whose own path holds only the failing provider. At get() it is thrown bare. The full text from errors() adds a Runtime cycle line when path is not empty.