nestjs/nest · error · InvalidClassException
ModuleRef cannot instantiate class (${value} is not construc
Error message
ModuleRef cannot instantiate class (${value} is not constructable). What it means
`ModuleRef.create(type)` instantiates a class that does NOT need to be registered in the DI container, but the argument must be a real constructor: a function with a prototype. InvalidClassException ('ModuleRef cannot instantiate class (X is not constructable)') means the value passed is undefined, a string token, an arrow function without prototype data, or an interface that compiled away to nothing.
Source
Thrown at packages/core/injector/module.ts:651
options: ModuleRefGetOrResolveOpts = {},
): Promise<TResult | Array<TResult>> {
options.strict ??= true;
options.each ??= false;
return this.resolvePerContext<TInput, TResult>(
typeOrToken,
self,
contextId,
options,
);
}
public async create<T = any>(
type: Type<T>,
contextId?: ContextId,
): Promise<T> {
if (!(type && isFunction(type) && type.prototype)) {
throw new InvalidClassException(type);
}
return this.instantiateClass<T>(type, self, contextId);
}
};
}
private isEntryProvider(metatype: InjectionToken): boolean {
return typeof metatype === 'function'
? !!Reflect.getMetadata(ENTRY_PROVIDER_WATERMARK, metatype)
: false;
}
private generateUuid(): string {
const prefix = 'M_';
const key = this.token
? this.token.includes(':')
? this.token.split(':')[1]
: this.tokenView on GitHub (pinned to a3a31b9643)
Solutions
- Pass the concrete imported class reference itself: `moduleRef.create(MyJobHandler)`.
- Ensure the import is a value import, not `import type` / interface-only, and that it is not undefined due to a circular import.
- If you only hold a string/symbol token, resolve a registered provider with `moduleRef.get(token)` instead of `create`.
- For arrow-function factories, register them as `{ provide, useFactory }` and let the container call them.
Example fix
// before
const handler = await this.moduleRef.create('email-job' as any);
// after
import { EmailJobHandler } from './jobs/email-job.handler';
const handler = await this.moduleRef.create(EmailJobHandler); Defensive patterns
Strategy: type-guard
Validate before calling
const isConstructable = (v: any): v is new (...args: any[]) => any =>
typeof v === 'function' && typeof v.prototype === 'object';
if (!isConstructable(targetType)) {
throw new Error(`Refusing moduleRef.create on non-class ${String(targetType)}`);
}
const instance = await this.moduleRef.create(targetType); Type guard
declare const TypeBrand: unique symbol;
function isConcreteClass<T>(v: unknown): v is abstract new (...args: any[]) => T {
return typeof v === 'function' && !!(v as any).prototype;
} Prevention
- Type the helper API as accepting `Type<T>` (newable) rather than any so the compiler rejects tokens/interfaces.
- Avoid `import type` for classes passed to create(); the value must exist at runtime.
- In strategy maps, store class references and run the isConstructable guard once at module load.
When it happens
Trigger: Calling `moduleRef.create(SomeInterface)` where the 'type' is only a TypeScript type; passing a variable that resolved to `undefined` (circular import or missing import); passing a string/symbol token used with `@Inject`; passing an already-created instance or a plain object; passing a lazy `() => Class` instead of the class itself.
Common situations: Dynamically instantiating strategies/handlers by class reference stored in config maps; code generators emitting type-only imports (`import type`); refactors where the class import got dropped but the reference remained typed as any.
Related errors
- Nest could not find ${name} element (this provider does not
- Nest can't resolve dependencies of the ${type.toString()}
- Nest could not select the given module ("${type.name}" does
AI-assisted analysis of nestjs/nest@a3a31b9643 (2026-08-21).
Data as JSON: /api/errors/6befcdd633daa98a.
Report an issue: GitHub.