This document provides detailed information about all error codes in Illuma and how to resolve them.
- Quick Reference
- Provider Errors (i100-i103)
- Alias Errors (i200-i201)
- Bootstrap Errors (i300-i302)
- Retrieval Errors (i400-i401)
- Instantiation Errors (i500-i504)
- Debugging Tips
| Code | Error | Quick Fix |
|---|---|---|
| i100 | Duplicate Provider | Remove duplicate or use MultiNodeToken |
| i101 | Duplicate Factory | Only provide one factory per token |
| i102 | Invalid Constructor | Add @NodeInjectable() decorator |
| i103 | Invalid Provider | Use valid provider syntax |
| i200 | Invalid Alias | Use token or decorated class |
| i201 | Loop Alias | Point alias to different token |
| i202 | Conflicting Strategies | Don't use self and skipSelf together |
| i300 | Not Bootstrapped | Call bootstrap() first |
| i301 | Container Bootstrapped | Provide before bootstrap() |
| i302 | Double Bootstrap | Only bootstrap once |
| i303 | Container destroyed | Container has been destroyed |
| i400 | Provider Not Found | Provide the token or use optional |
| i401 | Circular Dependency | Refactor to break cycle |
| i500 | Untracked Injection | Use in class field initializers only |
| i501 | Outside Context | Use only in fields/factories |
| i502 | Called Utils Outside | Use only during instantiation |
| i503 | Instance Access Failed | Check factory/constructor logic |
| i504 | Access Failed | Check provider configuration |
Error Message:
Duplicate provider for token "TokenName" detected.
Cause:
You attempted to register the same token multiple times with different providers (excluding MultiNodeToken, which is designed for multiple providers).
Example:
const CONFIG = new NodeToken<Config>('CONFIG');
container.provide({
provide: CONFIG,
value: { apiUrl: 'http://api1.com' }
});
// ❌ This will throw [i100]
container.provide({
provide: CONFIG,
value: { apiUrl: 'http://api2.com' }
});Solution:
- Remove the duplicate provider registration
- If you need multiple values, use
MultiNodeTokeninstead:
const CONFIGS = new MultiNodeToken<Config>('CONFIGS');
container.provide({
provide: CONFIGS,
value: { apiUrl: 'http://api1.com' }
});
container.provide({
provide: CONFIGS,
value: { apiUrl: 'http://api2.com' }
});
// ✅ This works with MultiNodeTokenError Message:
Tried to re-provide factory for token "TokenName" detected.
Cause: You attempted to provide a factory for a token that already has a factory defined. This can happen when:
- You provide the same decorated class multiple times with different factories
- You override a token's built-in factory
Example:
@NodeInjectable()
class MyService {
public value = 'original';
}
container.provide(MyService);
// ❌ This will throw [i101]
container.provide({
provide: MyService,
factory: () => new MyService()
});Solution: Only provide one factory per token:
@NodeInjectable()
class MyService {
public value = 'original';
}
// ✅ Only provide the class once
container.provide(MyService);For testing/overriding, use a different token:
const MY_SERVICE = new NodeToken<MyService>('MY_SERVICE');
// ✅ Original
container.provide({
provide: MY_SERVICE,
useClass: MyService
});
// ✅ For testing, create a new container with mock
const testContainer = new NodeContainer();
testContainer.provide({
provide: MY_SERVICE,
factory: () => new MockMyService()
});Error Message:
Cannot use constructor for token "ClassName". Please make sure to use @NodeInjectable() decorator
Cause:
You tried to provide a class directly to the container without marking it as injectable with the @NodeInjectable() decorator.
Example:
class MyService {
public doSomething() { }
}
// ❌ This will throw [i102]
container.provide(MyService);Solution:
Add the @NodeInjectable() decorator to your class:
@NodeInjectable()
class MyService {
public doSomething() { }
}
// ✅ This works
container.provide(MyService);Alternatively, use a token with a provider object:
const MY_SERVICE = new NodeToken<MyService>('MY_SERVICE');
container.provide({
provide: MY_SERVICE,
useClass: MyService
});
// ✅ This also worksError Message:
Cannot use provider as it is neither a NodeToken nor MultiNodeToken nor a valid constructor.
Cause:
You passed an invalid value to container.provide(). The provider must be one of:
- A
NodeTokenorMultiNodeToken - A class decorated with
@NodeInjectable() - A valid provider object with a
provideproperty
Example:
// ❌ All of these will throw [i103]
container.provide("some string");
container.provide(123);
container.provide({ invalid: 'object' });
container.provide(null);Solution: Use valid provider syntax:
// ✅ Using decorated class
@NodeInjectable()
class MyService { }
container.provide(MyService);
// ✅ Using token
const TOKEN = new NodeToken('TOKEN');
container.provide(TOKEN);
// ✅ Using provider object
container.provide({
provide: TOKEN,
value: 'some value'
});Error Message:
Invalid alias target "<value>". Alias must be a NodeToken, MultiNodeToken, or a class decorated with @NodeInjectable().
Cause:
You tried to create an alias using an invalid target. The alias property must be one of:
- A
NodeToken - A
MultiNodeToken - A class decorated with
@NodeInjectable()
Common mistakes:
- Using a plain string, number, or object as the alias
- Using an undecorated class
- Using a raw value instead of a token
Example:
const SERVICE_A = new NodeToken('SERVICE_A');
// ❌ This will throw [i200] - string is not valid
container.provide({
provide: SERVICE_A,
alias: 'some-string'
});
// ❌ This will throw [i200] - plain object is not valid
container.provide({
provide: SERVICE_A,
alias: { value: 'test' }
});
// ❌ This will throw [i200] - undecorated class
class MyService { }
container.provide({
provide: SERVICE_A,
alias: MyService // Missing @NodeInjectable()
});Solution: Use a valid token or decorated class as the alias target:
const SERVICE_A = new NodeToken('SERVICE_A');
const SERVICE_B = new NodeToken('SERVICE_B');
// ✅ Option 1: Alias to another token
container.provide({
provide: SERVICE_B,
useClass: MyService,
});
container.provide({
provide: SERVICE_A,
alias: SERVICE_B, // Valid: NodeToken
});
// ✅ Option 2: Alias to a decorated class
@NodeInjectable()
class MyService { }
container.provide(MyService);
container.provide({
provide: SERVICE_A,
alias: MyService // Valid: decorated class
});
// ✅ Option 3: Alias to a MultiNodeToken
const MULTI = new MultiNodeToken('MULTI');
container.provide({
provide: SERVICE_A,
alias: MULTI // Valid: MultiNodeToken
});Note: You don't need to provide the alias target before creating the alias (unlike what you might expect). The target will be resolved when the container is bootstrapped. However, if the alias target is never provided, you'll get an [i400] Provider Not Found error when trying to retrieve it.
Error Message:
Token "TokenName" cannot alias itself in a loop.
Cause: You tried to create a self-referential alias where a token points to itself.
Example:
const TOKEN = new NodeToken('TOKEN');
// ❌ This will throw [i201]
container.provide({
provide: TOKEN,
alias: TOKEN
});Solution: Ensure aliases point to different tokens:
const TOKEN_A = new NodeToken('TOKEN_A');
const TOKEN_B = new NodeToken('TOKEN_B');
container.provide({
provide: TOKEN_B,
value: 'some value'
});
// ✅ This works
container.provide({
provide: TOKEN_A,
alias: TOKEN_B
});Error Message:
Cannot retrieve providers before the container has been bootstrapped.
Cause:
You attempted to call container.get() before calling container.bootstrap().
Example:
const container = new NodeContainer();
const TOKEN = new NodeToken('TOKEN');
container.provide({
provide: TOKEN,
value: 'test'
});
// ❌ This will throw [i300]
const value = container.get(TOKEN);Solution:
Always call bootstrap() before retrieving providers:
const container = new NodeContainer();
const TOKEN = new NodeToken('TOKEN');
container.provide({
provide: TOKEN,
value: 'test'
});
// ✅ Bootstrap first
container.bootstrap();
// ✅ Now you can get providers
const value = container.get(TOKEN);Error Message:
Cannot modify providers after the container has been bootstrapped.
Cause:
You tried to register providers after calling container.bootstrap().
Example:
const container = new NodeContainer();
container.bootstrap();
const TOKEN = new NodeToken('TOKEN');
// ❌ This will throw [i301]
container.provide({
provide: TOKEN,
value: 'test'
});Solution: Register all providers before bootstrapping:
const container = new NodeContainer();
const TOKEN = new NodeToken('TOKEN');
// ✅ Provide before bootstrap
container.provide({
provide: TOKEN,
value: 'test'
});
// ✅ Bootstrap after all providers are registered
container.bootstrap();Error Message:
Container has already been bootstrapped and cannot be bootstrapped again.
Cause:
You called container.bootstrap() more than once on the same container instance.
Example:
const container = new NodeContainer();
container.bootstrap();
// ❌ This will throw [i302]
container.bootstrap();Solution:
Only call bootstrap() once per container:
const container = new NodeContainer();
// Register all providers
container.provide(/* ... */);
// ✅ Bootstrap once
container.bootstrap();
// Don't call bootstrap() againIf you need a fresh container, create a new instance:
function createContainer() {
const container = new NodeContainer();
// Register providers...
container.bootstrap();
return container;
}
const container1 = createContainer();
const container2 = createContainer(); // ✅ New instanceError Message:
Container has been already destroyed
Cause: You attempted to use an injector or the container it represents after it has been destroyed. Once a container is destroyed, it cannot be used to resolve dependencies, produce new instances, create child containers or be destroyed again.
Example:
const container = new NodeContainer();
const SomeToken = new NodeToken('SomeToken');
container.provide(SomeToken.withValue('test'));
container.destroy();
container.get(SomeToken); // ❌ This will throw [i303]Solution:
Make sure to only call destroy() when you are completely done with the container and its dependencies. Avoid using the container or any of its injectors after calling destroy().
Error Message:
No provider found for "TokenName".
Cause: You tried to retrieve a token that hasn't been registered in the container.
Example:
const container = new NodeContainer();
const TOKEN = new NodeToken('TOKEN');
container.bootstrap();
// ❌ This will throw [i400]
const value = container.get(TOKEN);Solution: Register the provider before bootstrapping:
const container = new NodeContainer();
const TOKEN = new NodeToken('TOKEN');
// ✅ Provide the token
container.provide({
provide: TOKEN,
value: 'test'
});
container.bootstrap();
// ✅ Now it can be retrieved
const value = container.get(TOKEN);For optional dependencies, use the optional flag:
@NodeInjectable()
class MyService {
// ✅ Returns null if not found instead of throwing
private readonly logger = nodeInject(Logger, { optional: true });
public doSomething() {
this.logger?.log('Doing something');
}
}Error Message:
Circular dependency detected while resolving "ProviderName":
ServiceA -> ServiceB -> ServiceA
Cause: Two or more services depend on each other in a circular way.
Example:
@NodeInjectable()
class ServiceA {
private readonly b = nodeInject(ServiceB);
}
@NodeInjectable()
class ServiceB {
private readonly a = nodeInject(ServiceA); // ❌ Circular!
}
container.provide(ServiceA);
container.provide(ServiceB);
container.bootstrap(); // ❌ This will throw [i401]Solution: Refactor to break the circular dependency:
Option 1: Extract shared logic
@NodeInjectable()
class SharedService {
public sharedMethod() { }
}
@NodeInjectable()
class ServiceA {
private readonly shared = nodeInject(SharedService);
}
@NodeInjectable()
class ServiceB {
private readonly shared = nodeInject(SharedService);
}
// ✅ No circular dependencyOption 2: Use events/callbacks
@NodeInjectable()
class ServiceA {
private callbacks: Array<() => void> = [];
public registerCallback(cb: () => void) {
this.callbacks.push(cb);
}
}
@NodeInjectable()
class ServiceB {
constructor() {
const serviceA = nodeInject(ServiceA);
serviceA.registerCallback(() => {
// Handle callback
});
}
}
// ✅ ServiceB depends on A, but A doesn't depend on BOption 3: Use defer Injection
You can use injectDefer to defer the resolution of one of the dependencies. This works because the dependency is not resolved until the function is called, breaking the cycle during instantiation in a cost of transparency on bootstrap.
@NodeInjectable()
class ServiceA {
// injectDefer returns a function () => ServiceB
private readonly injectB = injectDefer(ServiceB);
private get b() {
return this.injectB();
}
public someMethod() {
// Resolve dependency only when needed
this.b.method();
}
}
@NodeInjectable()
class ServiceB {
private readonly a = nodeInject(ServiceA);
}
// ✅ Initialization cycle is brokenError Message:
Cannot instantiate ParentName because it depends on untracked injection TokenName.
Please make sure all injections are properly tracked.
Cause:
You used nodeInject() outside of an injection context, or the dependency wasn't properly registered in the container's dependency tree.
Example:
@NodeInjectable()
class MyService {
// ❌ Don't use conditional injection – function may produce uncertain result and break tracking
private readonly _untracked = someFunction() ? nodeInject(Logger) : null;
private readonly _unreachable = nodeInject(getUntrackedToken());
// ❌ Don't call nodeInject in methods
public doSomething() {
const logger = nodeInject(Logger);
logger.log('test');
}
}Solution:
Only use nodeInject() during class initialization (in class field initializers):
@NodeInjectable()
class MyService {
// ✅ Inject in class field
private readonly logger = nodeInject(Logger);
public doSomething() {
// ✅ Use the injected dependency
this.logger.log('test');
}
}Make sure all dependencies are provided:
@NodeInjectable()
class Logger { }
@NodeInjectable()
class MyService {
private readonly logger = nodeInject(Logger);
}
// ✅ Provide all services
container.provide(Logger);
container.provide(MyService);
container.bootstrap();Error Message:
Cannot inject "TokenName" outside of an injection context.
Cause:
You tried to use nodeInject() outside of a valid injection context. nodeInject() can only be called:
- During class field initialization in injectable classes
- Inside factory functions provided to the container
Example:
// ❌ This will throw [i501] - top-level call
const logger = nodeInject(Logger);
@NodeInjectable()
class MyService {
constructor() {
// ✅ This is valid - in constructor
const logger = nodeInject(Logger);
}
public doSomething() {
// ❌ This is not – will throw [i501]
const logger = nodeInject(Logger);
}
}Solution:
Only use nodeInject() in class field initializers or factory functions:
@NodeInjectable()
class MyService {
// ✅ In class field initializer
private readonly logger = nodeInject(Logger);
public doSomething() {
// ✅ Use the injected field
this.logger.log('Doing something');
}
}
// ✅ In factory function
const TOKEN = new NodeToken<MyService>('TOKEN');
container.provide({
provide: TOKEN,
factory: () => {
const logger = nodeInject(Logger);
return new MyService(logger);
}
});Error Message:
Cannot call injection utilities outside of an injection context.
Cause: You attempted to call injection utility functions outside of a valid injection context. These utilities are only available during the dependency resolution phase.
Solution: Ensure utility functions are only called within:
- Factory functions
- Class field initializers in injectable classes
// ✅ Correct usage in factory
container.provide({
provide: TOKEN,
factory: () => {
// Utility calls here are in context
return createInstance();
}
});Error Message:
Failed to access instance for token "TokenName". It was not properly instantiated.
Cause: The container tried to retrieve an instance that wasn't properly instantiated. This typically indicates an internal error in the dependency resolution system.
Common causes:
- The dependency tree wasn't built correctly
- An instantiation callback failed silently
- The instance was garbage collected prematurely
Solution: This error usually indicates a bug in Illuma or a very unusual edge case. Try:
- Simplify your setup:
// Create a minimal reproduction
const container = new NodeContainer();
container.provide(OnlyTheFailingToken);
container.bootstrap();- Check for async issues:
// Ensure factories are synchronous
container.provide({
provide: TOKEN,
factory: () => {
// ❌ Don't use async/await in factories
// return await fetchData();
// ✅ Return synchronous values
return new MyService();
}
});- Report the issue: If the problem persists, please report it on GitHub with a minimal reproduction.
Error Message:
Failed to access the requested instance due to an unknown error.
Cause: A general instance access failure occurred that doesn't fit into other error categories. This is a catch-all error for unexpected situations.
Solution:
- Check your provider configuration for syntax errors
- Ensure all factories return valid values
- Verify that class constructors don't throw errors
- Review the full error stack trace for more details
If the issue persists, create a minimal reproduction and report it on GitHub.
const container = new NodeContainer({
measurePerformance: true
});This can help identify slow instantiation or resolution issues.
If you encounter resolution errors, trace your dependencies:
// Start with the failing service
@NodeInjectable()
class FailingService {
// List all dependencies
private readonly dep1 = nodeInject(Dependency1);
private readonly dep2 = nodeInject(Dependency2);
}
// Make sure each dependency is provided
container.provide(Dependency1);
container.provide(Dependency2);
container.provide(FailingService);Create minimal test cases to isolate the problem:
// Minimal reproduction
const container = new NodeContainer();
container.provide(OnlyTheFailingService);
container.bootstrap();
container.get(OnlyTheFailingService);If you encounter an error not covered here:
- Check the error code in the quick reference table
- Review the detailed section for your error code
- Create a minimal reproduction to isolate the issue
- Report issues: GitHub Issues
- Getting Started - Setup and basic concepts
- Providers Guide - Provider types
- Tokens Guide - Using tokens
- API Reference - Complete API documentation