{"record":{"id":"68df7df4e0aad195","repo":"mermaid-js/mermaid","slug":"sandbox-iframe-i-id-is-missing-its-content-docu","errorCode":null,"errorMessage":"Sandbox iframe #i${id} is missing its content document","messagePattern":"Sandbox iframe #i(.+?) is missing its content document","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mermaid/src/utils/diagramRoot.ts","lineNumber":22,"sourceCode":"export interface DiagramRoot {\n  /** Selection of the body that diagram elements should be queried from. */\n  root: D3HtmlSelection<HTMLElement>;\n  /** Owner document of {@link root} (the iframe document in sandbox mode). */\n  doc: Document;\n}\n\n/**\n * Resolves the root selection a renderer should draw into, accounting for\n * `securityLevel: 'sandbox'` where the diagram lives inside an `#i<id>`\n * iframe. Centralizes the sandbox handling that was previously copy-pasted\n * (with non-null assertions) into every renderer.\n */\nexport const getDiagramRoot = (id: string, securityLevel?: string): DiagramRoot => {\n  if (securityLevel === 'sandbox') {\n    const sandboxElement = select<HTMLIFrameElement, unknown>('#i' + id);\n    const doc = sandboxElement.node()?.contentDocument;\n    if (!doc) {\n      throw new Error(`Sandbox iframe #i${id} is missing its content document`);\n    }\n    return { root: select(doc.body) as unknown as D3HtmlSelection<HTMLElement>, doc };\n  }\n  return { root: select('body') as unknown as D3HtmlSelection<HTMLElement>, doc: document };\n};\n","sourceCodeStart":4,"sourceCodeEnd":28,"githubUrl":"https://github.com/mermaid-js/mermaid/blob/d93e9c88c01a599c062ee6a3f1462e3558ac6b90/packages/mermaid/src/utils/diagramRoot.ts#L4-L28","documentation":"Thrown by getDiagramRoot when securityLevel is 'sandbox' but the iframe element `#i<id>` either doesn't exist in the document or its contentDocument is null/undefined. In sandbox mode the diagram renders inside an iframe; getDiagramRoot selects `#i<id>` and reads .node().contentDocument to obtain the inner document. A missing iframe or a same-origin/cross-origin contentDocument that isn't accessible yields undefined.","triggerScenarios":"Calling getDiagramRoot(id, 'sandbox') before the sandbox iframe `#i<id>` has been injected into the DOM, when the iframe id doesn't match the diagram id, or when the iframe's contentDocument is null (cross-origin src, or not yet loaded). select returns an empty selection → .node() is null → optional chain yields undefined.","commonSituations":"Race condition: renderer runs before the iframe is created/loaded; a securityLevel mismatch (config says sandbox but the host page didn't create the iframe); cross-origin iframe src blocking contentDocument access; or an id mismatch between the mermaid diagram id and the iframe element id.","solutions":["Ensure the sandbox iframe with id `i${diagramId}` exists and is same-origin/loaded before rendering — let mermaid's own sandbox setup create it rather than calling getDiagramRoot standalone.","Verify securityLevel is only 'sandbox' when the host environment actually injects the iframe; otherwise use the default 'strict' level.","If managing the iframe manually, set its src to a same-origin blank document (srcdoc or about:blank) so contentDocument is accessible.","Await the iframe's load event before invoking the renderer."],"exampleFix":"// before — calling renderer before iframe ready / wrong id\ngetDiagramRoot('myDiagram', 'sandbox'); // #imyDiagram missing → throws\n// after\nconst iframe = document.getElementById('i' + diagramId) as HTMLIFrameElement;\nawait new Promise((res) => iframe.addEventListener('load', res, { once: true }));\ngetDiagramRoot(diagramId, 'sandbox');","handlingStrategy":"validation","validationCode":"function getSandboxDoc(id: string): Document | null {\n  const iframe = document.getElementById('i' + id) as HTMLIFrameElement | null;\n  return iframe?.contentDocument ?? null;\n}\nif (securityLevel === 'sandbox' && !getSandboxDoc(id)) throw new Error(`sandbox iframe #i${id} not ready`);","typeGuard":"function isIframeReady(id: string): boolean {\n  const f = document.getElementById('i' + id) as HTMLIFrameElement | null;\n  return !!f && !!f.contentDocument;\n}","tryCatchPattern":"try { return getDiagramRoot(id, securityLevel); } catch (e) { if (/missing its content document/.test(String(e))) { await waitForIframe(id); return getDiagramRoot(id, securityLevel); } throw e; }","preventionTips":["Let mermaid create the sandbox iframe; don't call getDiagramRoot standalone.","Await the iframe load event before rendering.","Keep the iframe same-origin (srcdoc/about:blank) so contentDocument is readable."],"tags":["sandbox","security","dom","iframe","render"],"backgroundTag":null,"analyzedSha":"d93e9c88c01a599c062ee6a3f1462e3558ac6b90","analyzedAt":"2026-08-12T06:23:11.304Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}