BabylonJS/Babylon.js · error
A service producing the contract '${contract.toString()}' ha
Error message
A service producing the contract '${contract.toString()}' has already been added to this '${this._friendlyName}' container. What it means
ServiceContainer._addService rejects a service definition whose `produces` array contains a contract (symbol) already mapped in this container's _serviceDefinitions. The container enforces one-producer-per-contract per container so dependency resolution stays unambiguous. It is a registration-time guard, thrown synchronously from addServices.
Source
Thrown at packages/dev/sharedUiComponents/src/modularTool/modularity/serviceContainer.ts:106
}
/**
* Registers a service definition in the service container.
* @param serviceDefinition The service definition to register.
* @returns A disposable that will remove the service definition from the service container.
*/
public addService<Produces extends IService<symbol>[] = [], Consumes extends IService<symbol>[] = []>(serviceDefinition: ServiceDefinition<Produces, Consumes>): IDisposable {
return this.addServices(serviceDefinition);
}
private _addService(service: WeaklyTypedServiceDefinition) {
if (this._isDisposed) {
throw new Error(`'${this._friendlyName}' container is disposed.`);
}
service.produces?.forEach((contract) => {
if (this._serviceDefinitions.has(contract)) {
throw new Error(`A service producing the contract '${contract.toString()}' has already been added to this '${this._friendlyName}' container.`);
}
});
service.produces?.forEach((contract) => {
this._serviceDefinitions.set(contract, service);
});
const dependencies = service.consumes?.map((contract) => this._resolveDependency(contract, service)) ?? [];
this._serviceInstances.set(service, service.factory(...dependencies));
}
/**
* Resolves a dependency by contract identity for a consuming service.
* Checks local services first, then walks up the parent chain.
* Registers the consumer as a dependent in whichever container owns the dependency.
* @param contract The contract identity to resolve.
* @param consumer The service definition that consumes this dependency.
View on GitHub (pinned to 0592b347b8)
Solutions
- Remove the duplicate addServices call or guard it with a check of which contracts are already registered before adding.
- If two services must share a contract, have only one produce it and the other consume it (list it in consumes, not produces).
- For child-container scenarios, add the second producer to a child container instead of the same container.
- Ensure contract symbols come from a single shared module so identity is unique and intentional.
Example fix
// before
container.addServices(myService); // myService.produces = [FooContract]
container.addServices(myService); // throws: contract already added
// after
if (!container.hasService?.(FooContract)) {
container.addServices(myService);
} Defensive patterns
Strategy: validation
Validate before calling
// assume a registry snapshot or helper exists; otherwise track locally
const registered = new Set();
function safeAdd(container, service) {
if (service.produces?.some((c) => registered.has(c))) return; // skip duplicates
container.addServices(service);
service.produces?.forEach((c) => registered.add(c));
} Try / catch
// try {
// container.addServices(service);
// } catch (e) {
// if (!String(e.message).includes("has already been added")) throw e;
// // treat as idempotent re-registration
// } Prevention
- Own contract symbols in one shared module and import them everywhere.
- Wrap addServices in an idempotent helper for hot-reload paths.
- One producer per contract; consumers list contracts in consumes only.
When it happens
Trigger: Calling container.addServices(service) where service.produces contains a contract symbol already produced by a previously added service in the same container; adding the same service instance twice; two modules declaring the same contract symbol (e.g. both export a symbol with the same registry key).
Common situations: Two plugin modules each exporting their own contract symbol intended to be 'the same' service; duplicate addServices calls after hot-reload or re-initialization of a modular tool extension; copy-pasting a service definition that already exists in the host container.
Related errors
- Service '${contract.toString()}' has not been registered in
- Function not injected yet. Use the factory to create the plu
- loadFileInjection is not defined
- Service '${contract.toString()}' has not been instantiated i
- Service '${service.friendlyName}' has dependents: ${Array.fr
AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30).
Data as JSON: /api/errors/205dc39a82a878ae.
Report an issue: GitHub.