{"record":{"id":"205dc39a82a878ae","repo":"BabylonJS/Babylon.js","slug":"a-service-producing-the-contract-contract-tostr","errorCode":null,"errorMessage":"A service producing the contract '${contract.toString()}' has already been added to this '${this._friendlyName}' container.","messagePattern":"A service producing the contract '(.+?)' has already been added to this '(.+?)' container\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/sharedUiComponents/src/modularTool/modularity/serviceContainer.ts","lineNumber":106,"sourceCode":"    }\r\n\r\n    /**\r\n     * Registers a service definition in the service container.\r\n     * @param serviceDefinition The service definition to register.\r\n     * @returns A disposable that will remove the service definition from the service container.\r\n     */\r\n    public addService<Produces extends IService<symbol>[] = [], Consumes extends IService<symbol>[] = []>(serviceDefinition: ServiceDefinition<Produces, Consumes>): IDisposable {\r\n        return this.addServices(serviceDefinition);\r\n    }\r\n\r\n    private _addService(service: WeaklyTypedServiceDefinition) {\r\n        if (this._isDisposed) {\r\n            throw new Error(`'${this._friendlyName}' container is disposed.`);\r\n        }\r\n\r\n        service.produces?.forEach((contract) => {\r\n            if (this._serviceDefinitions.has(contract)) {\r\n                throw new Error(`A service producing the contract '${contract.toString()}' has already been added to this '${this._friendlyName}' container.`);\r\n            }\r\n        });\r\n\r\n        service.produces?.forEach((contract) => {\r\n            this._serviceDefinitions.set(contract, service);\r\n        });\r\n\r\n        const dependencies = service.consumes?.map((contract) => this._resolveDependency(contract, service)) ?? [];\r\n\r\n        this._serviceInstances.set(service, service.factory(...dependencies));\r\n    }\r\n\r\n    /**\r\n     * Resolves a dependency by contract identity for a consuming service.\r\n     * Checks local services first, then walks up the parent chain.\r\n     * Registers the consumer as a dependent in whichever container owns the dependency.\r\n     * @param contract The contract identity to resolve.\r\n     * @param consumer The service definition that consumes this dependency.\r","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/sharedUiComponents/src/modularTool/modularity/serviceContainer.ts#L88-L124","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\ncontainer.addServices(myService); // myService.produces = [FooContract]\ncontainer.addServices(myService); // throws: contract already added\n// after\nif (!container.hasService?.(FooContract)) {\n    container.addServices(myService);\n}","handlingStrategy":"validation","validationCode":"// assume a registry snapshot or helper exists; otherwise track locally\nconst registered = new Set();\nfunction safeAdd(container, service) {\n  if (service.produces?.some((c) => registered.has(c))) return; // skip duplicates\n  container.addServices(service);\n  service.produces?.forEach((c) => registered.add(c));\n}","typeGuard":null,"tryCatchPattern":"// try {\n//   container.addServices(service);\n// } catch (e) {\n//   if (!String(e.message).includes(\"has already been added\")) throw e;\n//   // treat as idempotent re-registration\n// }","preventionTips":["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."],"tags":["dependency-injection","duplicate-registration","modularity"],"backgroundTag":"duplicate-service-contract","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}