Skip to Content
ErrorsNEXUS_ASYNC_TRANSIENT

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_ASYNC_TRANSIENT

NEXUS_ASYNC_TRANSIENT means a factory with lifetime: 'transient' returned a promise when the container built it. A transient builds at each request and get() is synchronous, so it cannot wait. get(), resolve(), the get() of a scope and a lazy() thunk throw it directly. When a provider that builds at create depends on the transient, create rejects with NEXUS_PROVIDER_FAILED and this error is its cause.

Reproduce NEXUS_ASYNC_TRANSIENT

TypeScript rejects an async transient factory where you write it. It reports TS2769 on the provide( call, and the error for the factory overload names this code:

Fails the type check

provide(DRONE, { useFactory: async () => ({ serial: 7 }), lifetime: 'transient', }); // TS2769: No overload matches this call. The error for the factory overload ends with: // Type '"transient"' is not assignable to type '"singleton" | "scoped" | "NEXUS_ASYNC_TRANSIENT: get() is synchronous, so a transient factory cannot be async. Use lifetime: 'scoped', or make the token a function type and provide () => Promise<T>" | undefined'.

The error reaches run time when the types cannot see the promise, as with a JavaScript helper whose typings are wrong. The region uses such a helper. 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 ISurveyDrone { readonly serial: number; } const DRONE = new Token<ISurveyDrone>('SurveyDrone'); // launchDrone stands in for a JavaScript helper whose typings say it returns // a drone. At run time it returns a promise, which TypeScript cannot see. const launchDrone = (async (serial: number) => ({ serial })) as unknown as ( serial: number, ) => ISurveyDrone; const Tactical = defineModule({ name: 'Tactical', providers: [ provide(DRONE, { useFactory: () => launchDrone(7), lifetime: 'transient', }), ], exports: [DRONE], }); const getDrone = (ship: Nexus) => { try { return ship.get(DRONE); } catch (error) { return error; } }; await using ship = await Nexus.create(Tactical); const thin = getDrone(ship); const line = isNexusError(thin) ? thin.message : null; // -> '[NEXUS_ASYNC_TRANSIENT] token=SurveyDrone module=Tactical. https://nexus.js.org/errors/NEXUS_ASYNC_TRANSIENT' console.log(line); await using fullShip = await Nexus.create(Tactical, { plugins: [errors()] }); const full = getDrone(fullShip); const text = isNexusError(full) ? full.message.split('\n') : null; // -> ['[NEXUS_ASYNC_TRANSIENT] SurveyDrone (module Tactical) is transient and its factory returned a promise. get() is synchronous and cannot wait for it.', " Fix: use lifetime: 'scoped', or make the token a function type and provide () => Promise<T>."] 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. Then save the fix region below as fixed.ts and run npx tsx fixed.ts. It prints 7.

Fix NEXUS_ASYNC_TRANSIENT

NEXUS_ASYNC_TRANSIENT is fixed by giving the factory a lifetime that the container builds ahead of get(): singleton, built at create, or scoped, built at createScope. Here the drone is scoped, and the program asks a scope for it. To start async work on every call, make the token a function type and provide () => Promise<T>.

import { Nexus, Token, defineModule, provide } from '@nexusdi/core'; interface ISurveyDrone { readonly serial: number; } const DRONE = new Token<ISurveyDrone>('SurveyDrone'); async function launchDrone(serial: number): Promise<ISurveyDrone> { return { serial }; } const Tactical = defineModule({ name: 'Tactical', providers: [ provide(DRONE, { useFactory: () => launchDrone(7), lifetime: 'scoped', }), ], exports: [DRONE], }); await using ship = await Nexus.create(Tactical); await using shuttle = await ship.createScope(); const serial = shuttle.get(DRONE).serial; // -> 7 console.log(serial);

Try it: in fixed.ts, change lifetime: 'scoped' to lifetime: 'transient' and run it again. tsc reports TS2769 on the provide( call, with the message shown above. tsx skips the type check, and shuttle.get(DRONE) throws an uncaught AsyncTransientError that starts with [NEXUS_ASYNC_TRANSIENT] token=SurveyDrone module=Tactical.. Put 'scoped' back and the program prints 7 again.

Fields of AsyncTransientError

AsyncTransientError carries these fields:

  • token: the display name of the transient provider.
  • module: its module.