{"record":{"id":"285f2d5eb463e53a","repo":"emberjs/ember.js","slug":"cannot-call-lookup-fullname-after-the-own","errorCode":null,"errorMessage":"Cannot call `.lookup('${fullName}')` after the owner has been destroyed","messagePattern":"Cannot call `\\.lookup\\('(.+?)'\\)` after the owner has been destroyed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@ember/-internals/container/lib/container.ts","lineNumber":158,"sourceCode":"   let registry = new Registry();\n   let container = registry.container();\n    registry.register('api:twitter', Twitter);\n    let twitter = container.lookup('api:twitter', { singleton: false });\n   let twitter2 = container.lookup('api:twitter', { singleton: false });\n    twitter === twitter2; //=> false\n   ```\n    @private\n   @method lookup\n   @param {String} fullName\n   @param {RegisterOptions} [options]\n   @return {any}\n   */\n  lookup(\n    fullName: string,\n    options?: RegisterOptions\n  ): InternalFactory<object> | object | undefined {\n    if (this.isDestroyed) {\n      throw new Error(`Cannot call \\`.lookup('${fullName}')\\` after the owner has been destroyed`);\n    }\n    assert('fullName must be a proper full name', this.registry.isValidFullName(fullName));\n    return lookup(this, this.registry.normalize(fullName), options);\n  }\n\n  /**\n   A depth first traversal, destroying the container, its descendant containers and all\n   their managed objects.\n    @private\n   @method destroy\n   */\n  destroy(): void {\n    this.isDestroying = true;\n\n    destroyDestroyables(this);\n  }\n\n  finalizeDestroy(): void {","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/emberjs/ember.js/blob/26f97246a8bf2e28edf26ac3093da2e86c04ffc5/packages/@ember/-internals/container/lib/container.ts#L140-L176","documentation":"Container#lookup refuses to resolve any factory once the owner (application/instance) has been destroyed. Ember destroys owners during teardown (e.g. app.destroy() or test teardown); calling into a destroyed container is always a lifecycle bug, so it throws instead of returning stale objects.","triggerScenarios":"Calling owner.lookup('service:foo') (directly or via controllerFor) after the owner's destroy() has run — e.g. in a setTimeout/async callback, an event listener not cleaned up, or code running after application.destroy().","commonSituations":"Tests hitting the error after teardown due to unresolved promises or lingering timers; app teardown while background jobs still call lookup; accessing a cached owner reference after restart in fastboot or engine teardown.","solutions":["Ensure all async work referencing the owner completes or is cancelled before destroy()","Re-check lifecycle: only call lookup while the app/instance is running","Remove or tear down event listeners/timers in willDestroy","In tests, await settled state before teardown (use ember-test-waiters / settled())"],"exampleFix":"// before\nsetTimeout(() => this.owner.lookup('service:session'), 5000);\n// after\ntimer = setTimeout(() => {\n  if (!this.isDestroying) this.owner.lookup('service:session');\n}, 5000);","handlingStrategy":"validation","validationCode":"if (owner.isDestroying || owner.isDestroyed) return; // skip lookup","typeGuard":"function isOwnerLive(owner) { return !owner.isDestroyed && !owner.isDestroying; }","tryCatchPattern":"try { return owner.lookup('service:foo'); } catch (e) { if (String(e.message).includes('after the owner has been destroyed')) return null; throw e; }","preventionTips":["Check isDestroying/isDestroyed before owner access in async code","Clean up timers/listeners in willDestroy","Await settled() in tests before teardown","Avoid caching owner references across lifecycle boundaries"],"tags":["ember","lifecycle","container","destroyed-owner"],"backgroundTag":"lookup-after-destroy","analyzedSha":"26f97246a8bf2e28edf26ac3093da2e86c04ffc5","analyzedAt":"2026-09-01T05:01:28.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}