{"record":{"id":"1286e3db355217c7","repo":"angular/components","slug":"host-already-has-a-portal-attached","errorCode":null,"errorMessage":"Host already has a portal attached","messagePattern":"Host already has a portal attached","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cdk/portal/portal-errors.ts","lineNumber":22,"sourceCode":" *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/**\n * Throws an exception when attempting to attach a null portal to a host.\n * @docs-private\n */\nexport function throwNullPortalError() {\n  throw Error('Must provide a portal to attach');\n}\n\n/**\n * Throws an exception when attempting to attach a portal to a host that is already attached.\n * @docs-private\n */\nexport function throwPortalAlreadyAttachedError() {\n  throw Error('Host already has a portal attached');\n}\n\n/**\n * Throws an exception when attempting to attach a portal to an already-disposed host.\n * @docs-private\n */\nexport function throwPortalOutletAlreadyDisposedError() {\n  throw Error('This PortalOutlet has already been disposed');\n}\n\n/**\n * Throws an exception when attempting to attach an unknown portal type.\n * @docs-private\n */\nexport function throwUnknownPortalTypeError() {\n  throw Error(\n    'Attempting to attach an unknown Portal type. BasePortalOutlet accepts either ' +\n      'a ComponentPortal or a TemplatePortal.',","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/portal/portal-errors.ts#L4-L40","documentation":"BasePortalOutlet.attach calls `throwPortalAlreadyAttachedError` when `_attachedPortal` is already set — one outlet can hold only one portal at a time. You must detach the current portal before attaching a new one.","triggerScenarios":"Calling `attach` twice on the same outlet without `detach()`; attaching from both an @Input setter and a lifecycle hook; multiple components writing to a shared `<ng-template cdkPortalOutlet>`.","commonSituations":"Reactive updates that re-trigger attach on every emission without detaching; two directives/components competing for the same outlet; rapid state changes (typing, routing) that call attach repeatedly.","solutions":["Call `outlet.detach()` before `outlet.attach(newPortal)`","Track attached state and skip attach if the same portal is already attached","Use `hasAttached()` to check before attaching","In components, detach in ngOnDestroy and before re-attachment on input changes"],"exampleFix":"// before\nthis.outlet.attach(portal);\n// ...later\nthis.outlet.attach(otherPortal); // throws\n// after\nif (this.outlet.hasAttached()) { this.outlet.detach(); }\nthis.outlet.attach(otherPortal);","handlingStrategy":"try-catch","validationCode":"if (!outlet.hasAttached()) outlet.attach(portal);","typeGuard":null,"tryCatchPattern":"try { outlet.attach(portal); } catch (e) { if (String(e.message).includes('already has a portal attached')) outlet.detach(); outlet.attach(portal); } else { throw e; } }","preventionTips":["Detach before attach","Use hasAttached() checks","Single owner per outlet"],"tags":["angular","cdk","portal","lifecycle"],"backgroundTag":"portal-already-attached","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}