{"record":{"id":"d9c15791c2b60884","repo":"wavetermdev/waveterm","slug":"layout-model-not-found","errorCode":null,"errorMessage":"Layout model not found","messagePattern":"Layout model not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/app/store/tabrpcclient.ts","lineNumber":25,"sourceCode":"import { WorkspaceLayoutModel } from \"@/app/workspace/workspace-layout-model\";\nimport { getLayoutModelForStaticTab } from \"@/layout/index\";\nimport { base64ToArrayBuffer } from \"@/util/util\";\nimport { RpcResponseHelper, WshClient } from \"./wshclient\";\nimport { RpcApi } from \"./wshclientapi\";\n\nexport class TabClient extends WshClient {\n    constructor(routeId: string) {\n        super(routeId);\n    }\n\n    handle_captureblockscreenshot(rh: RpcResponseHelper, data: CommandCaptureBlockScreenshotData): Promise<string> {\n        return this.captureBlockScreenshot(data.blockid);\n    }\n\n    async captureBlockScreenshot(blockId: string): Promise<string> {\n        const layoutModel = getLayoutModelForStaticTab();\n        if (!layoutModel) {\n            throw new Error(\"Layout model not found\");\n        }\n\n        const node = layoutModel.getNodeByBlockId(blockId);\n        if (!node) {\n            throw new Error(`Block not found: ${blockId}`);\n        }\n\n        const displayContainer = layoutModel.displayContainerRef.current;\n        if (!displayContainer) {\n            throw new Error(\"Display container not found\");\n        }\n\n        const containerRect = displayContainer.getBoundingClientRect();\n        const additionalProps = layoutModel.getNodeAdditionalProperties(node);\n\n        let electronRect: Electron.Rectangle;\n\n        if (!additionalProps?.rect) {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/frontend/app/store/tabrpcclient.ts#L7-L43","documentation":"captureBlockScreenshot needs the current static tab's LayoutModel to locate a block's DOM node and compute its on-screen rectangle for Electron's capturePage. getLayoutModelForStaticTab() returned null, meaning no layout model is registered for the active static tab. This is a lifecycle/state error: the screenshot RPC was invoked when the tab's layout system was not initialized or has been torn down.","triggerScenarios":"handle_captureblockscreenshot RPC arrives before the tab's React layout has mounted, after the tab/window was closed, or in a context (e.g. a preview/test static tab) where getLayoutModelForStaticTab() has no registered model.","commonSituations":"Client calls captureblockscreenshot right after tab switch/creation before mount completes; screenshotting a block in a closed window; running in an environment (storybook/tests/web) where the static-tab layout model is never set.","solutions":["Check that the block's tab is open and its layout has fully mounted before calling the RPC","Retry after a short delay (e.g. wait for the tab to render) if called during tab transitions","Confirm the call is routed to the correct tab's TabRpcClient, not a stale/closed one","If screenshotting many blocks, fetch the layout model once after confirming it exists rather than during transitions"],"exampleFix":"// before\nconst layoutModel = getLayoutModelForStaticTab();\nif (!layoutModel) {\n    throw new Error(\"Layout model not found\");\n}\n// after\nconst layoutModel = getLayoutModelForStaticTab();\nif (!layoutModel) {\n    await waitForLayoutModel(500);\n    layoutModel = getLayoutModelForStaticTab();\n}\nif (!layoutModel) {\n    throw new Error(\"Layout model not found\");\n}","handlingStrategy":"retry","validationCode":"const layoutModel = getLayoutModelForStaticTab();\nif (layoutModel == null) {\n    throw new Error(\"cannot capture screenshot: tab layout not mounted yet\");\n}","typeGuard":"function hasLayoutModel(): boolean {\n    return getLayoutModelForStaticTab() != null;\n}","tryCatchPattern":"try {\n    const pngData = await rpcService.call(\"captureblockscreenshot\", { blockid });\n} catch (e) {\n    if (String(e).includes(\"Layout model not found\")) {\n        await delay(150);\n        pngData = await rpcService.call(\"captureblockscreenshot\", { blockid });\n    } else {\n        throw e;\n    }\n}","preventionTips":["Gate screenshot APIs behind tab-mounted state","Cancel pending screenshot RPCs when tabs/windows close","Avoid layout-dependent RPCs during tab transitions","Add a readiness flag/event emitted once the layout model registers"],"tags":["lifecycle","frontend","screenshot"],"backgroundTag":"layout-model-not-initialized","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}