Skip to Content
ErrorsNEXUS_CIRCULAR_DEPENDENCY

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_CIRCULAR_DEPENDENCY

NEXUS_CIRCULAR_DEPENDENCY means providers depend on each other in a cycle and no edge of the cycle is a lazy() dependency, so no provider in it can be built first. Nexus.create reports it inside NEXUS_BLUEPRINT_INVALID.

The error covers indirect cycles of any length, across modules too. Why lazy() is opt-in says why NexusDI reports them.

Reproduce NEXUS_CIRCULAR_DEPENDENCY

The region has PlasmaRouter, bound to PowerRouter, depend on ShieldGrid, and DeflectorGrid, bound to ShieldGrid, depend on PowerRouter. Without a plugin, core prints 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 same error carries the full text and its fix.

import { Nexus, Token, defineModule, isNexusError } from '@nexusdi/core'; import { provide } 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 = [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; } } const Engineering = defineModule({ name: 'Engineering', providers: [ provide(POWER_ROUTER, { useClass: PlasmaRouter }), provide(SHIELD_GRID, { useClass: DeflectorGrid }), ], }); const thin = await Nexus.create(Engineering).catch((error: unknown) => error); if (!isNexusError(thin, 'NEXUS_BLUEPRINT_INVALID')) throw thin; const line = thin.errors[0]?.message; // -> '[NEXUS_CIRCULAR_DEPENDENCY] path=PowerRouter,ShieldGrid,PowerRouter. https://nexus.js.org/errors/NEXUS_CIRCULAR_DEPENDENCY' console.log(line); const full = await Nexus.create(Engineering, { plugins: [errors()] }).catch( (error: unknown) => error, ); if (!isNexusError(full, 'NEXUS_BLUEPRINT_INVALID')) throw full; const text = full.errors[0]?.message.split('\n'); // -> ['[NEXUS_CIRCULAR_DEPENDENCY] PowerRouter → ShieldGrid → PowerRouter is a dependency cycle.', ' Fix: wrap one edge in lazy(), for example the dependency of PowerRouter on ShieldGrid: lazy(ShieldGrid).'] 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 lines. The path field lists the cycle from its first provider back to it. Then save the fix region below as fixed.ts and run npx tsx fixed.ts. It prints 0.4.

Fix NEXUS_CIRCULAR_DEPENDENCY

NEXUS_CIRCULAR_DEPENDENCY is fixed by wrapping one edge of the cycle in lazy(). The dependency then receives a function that returns the instance, and the method calls it after startup. Here PlasmaRouter takes lazy(SHIELD_GRID). Lazy Dependencies and Cycles covers the rule.

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: change lazy(SHIELD_GRID) in the deps of PlasmaRouter back to SHIELD_GRID in fixed.ts and run it again. tsc flags the provide(POWER_ROUTER, ...) call, because the constructor still expects a function. Nexus.create rejects with a BlueprintError whose message lists [NEXUS_CIRCULAR_DEPENDENCY] path=PowerRouter,ShieldGrid,PowerRouter. Restore lazy(SHIELD_GRID) and the program prints 0.4 again.

Fields of CircularDependencyError

CircularDependencyError has one field:

  • path: the display names along the cycle, ending with the first one again.