{"record":{"id":"297a309099923a05","repo":"angular/components","slug":"must-provide-a-portal-to-attach","errorCode":null,"errorMessage":"Must provide a portal to attach","messagePattern":"Must provide a portal to attach","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cdk/portal/portal-errors.ts","lineNumber":14,"sourceCode":"/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\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","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/portal/portal-errors.ts#L1-L32","documentation":"`throwNullPortalError` is called by BasePortalOutlet.attach when a null/undefined portal is passed. The outlet has nothing to render, so the library fails fast with a clear message instead of a downstream null dereference.","triggerScenarios":"Calling `outlet.attach(null)` / `attach(undefined)`, or passing a variable that is undefined due to a failed lookup (e.g. `this.portals[key]` or an unresolved service member).","commonSituations":"Dynamic portal lookup returning undefined; a portal input @Input() not yet set when attach runs; optional chaining / async data not loaded before attach; refactors renaming the portal field.","solutions":["Guard before attaching: `if (portal) outlet.attach(portal)`","Ensure the @Input()/async data producing the portal is resolved before attach","Fix the lookup expression that returns null/undefined","Centralize attachment in a helper that validates the portal argument"],"exampleFix":"// before\nthis.outlet.attach(this.activePortal); // activePortal can be undefined\n// after\nif (this.activePortal) { this.outlet.attach(this.activePortal); }","handlingStrategy":"validation","validationCode":"function attachSafe(outlet: PortalOutlet, portal: Portal<any> | null | undefined): void {\n  if (portal) { outlet.attach(portal); }\n}","typeGuard":"function isPortal(v: unknown): v is Portal<any> {\n  return v instanceof Portal;\n}","tryCatchPattern":"try {\n  outlet.attach(portal!);\n} catch (e) {\n  if (e.message === 'Must provide a portal to attach') {\n    console.warn('No portal available; skipping attach');\n  } else { throw e; }\n}","preventionTips":["Initialize portal inputs with defaults instead of undefined","Null-check before attach, especially with async/dynamic portal lookups","Avoid non-null assertions (!) on portal variables","Log/inspect why the lookup produced null when it happens"],"tags":["angular","cdk","portal","null-check"],"backgroundTag":"null-portal-attach","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}