wavetermdev/waveterm · error

Layout model not found

Error message

Layout model not found

What it means

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.

Solutions

  1. Check that the block's tab is open and its layout has fully mounted before calling the RPC
  2. Retry after a short delay (e.g. wait for the tab to render) if called during tab transitions
  3. Confirm the call is routed to the correct tab's TabRpcClient, not a stale/closed one
  4. If screenshotting many blocks, fetch the layout model once after confirming it exists rather than during transitions

Example fix

// before
const layoutModel = getLayoutModelForStaticTab();
if (!layoutModel) {
    throw new Error("Layout model not found");
}
// after
const layoutModel = getLayoutModelForStaticTab();
if (!layoutModel) {
    await waitForLayoutModel(500);
    layoutModel = getLayoutModelForStaticTab();
}
if (!layoutModel) {
    throw new Error("Layout model not found");
}
Defensive patterns

Strategy: retry

Validate before calling

const layoutModel = getLayoutModelForStaticTab();
if (layoutModel == null) {
    throw new Error("cannot capture screenshot: tab layout not mounted yet");
}

Type guard

function hasLayoutModel(): boolean {
    return getLayoutModelForStaticTab() != null;
}

Try / catch

try {
    const pngData = await rpcService.call("captureblockscreenshot", { blockid });
} catch (e) {
    if (String(e).includes("Layout model not found")) {
        await delay(150);
        pngData = await rpcService.call("captureblockscreenshot", { blockid });
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/d9c15791c2b60884. Report an issue: GitHub.

Appendix: source

Thrown at frontend/app/store/tabrpcclient.ts:25

import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
import { getLayoutModelForStaticTab } from "@/layout/index";
import { base64ToArrayBuffer } from "@/util/util";
import { RpcResponseHelper, WshClient } from "./wshclient";
import { RpcApi } from "./wshclientapi";

export class TabClient extends WshClient {
    constructor(routeId: string) {
        super(routeId);
    }

    handle_captureblockscreenshot(rh: RpcResponseHelper, data: CommandCaptureBlockScreenshotData): Promise<string> {
        return this.captureBlockScreenshot(data.blockid);
    }

    async captureBlockScreenshot(blockId: string): Promise<string> {
        const layoutModel = getLayoutModelForStaticTab();
        if (!layoutModel) {
            throw new Error("Layout model not found");
        }

        const node = layoutModel.getNodeByBlockId(blockId);
        if (!node) {
            throw new Error(`Block not found: ${blockId}`);
        }

        const displayContainer = layoutModel.displayContainerRef.current;
        if (!displayContainer) {
            throw new Error("Display container not found");
        }

        const containerRect = displayContainer.getBoundingClientRect();
        const additionalProps = layoutModel.getNodeAdditionalProperties(node);

        let electronRect: Electron.Rectangle;

        if (!additionalProps?.rect) {

View on GitHub (pinned to a4447c1563)