wavetermdev/waveterm · error
Block not found: ${blockId}
Error message
Block not found: ${blockId} What it means
captureBlockScreenshot resolved the tab's LayoutModel, but layoutModel.getNodeByBlockId(blockId) returned null — the given blockId does not exist in this tab's layout tree. The screenshot can only be taken from blocks actually rendered in the current tab, so an unknown blockId is rejected.
Source
Thrown at frontend/app/store/tabrpcclient.ts:30
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) {
// Bug: rect is not set when there is only one block in the layout
// In this case, use the full container rect
electronRect = {
x: Math.round(containerRect.x),
y: Math.round(containerRect.y),View on GitHub (pinned to a4447c1563)
Solutions
- Verify the blockId still exists (via the tab's block list / getfocusedblockdata) before calling
- Ensure the RPC is sent on the TabRpcClient of the tab that owns the block
- Listen for block-close/delete events and purge cached blockIds
- Log the tabId+blockId pair to confirm they match the tab receiving the RPC
Example fix
// before
await rpcService.call("captureblockscreenshot", { blockid: cachedBlockId });
// after
if (globalStore.get(tabLayoutModel.nodeMap).some((n) => n.data?.blockId === cachedBlockId)) {
await rpcService.call("captureblockscreenshot", { blockid: cachedBlockId });
} else {
cachedBlockId = null;
} Defensive patterns
Strategy: validation
Validate before calling
const node = layoutModel?.getNodeByBlockId(blockId);
if (!node) {
console.warn("skipping capture, block not in this tab:", blockId);
return null;
} Type guard
function blockExistsInTab(blockId: string): boolean {
return getLayoutModelForStaticTab()?.getNodeByBlockId(blockId) != null;
} Try / catch
try {
const pngData = await rpcService.call("captureblockscreenshot", { blockid });
} catch (e) {
if (String(e).startsWith("Block not found:")) {
purgeCachedBlockId(blockid);
return null;
}
throw e;
} Prevention
- Subscribe to block close/delete events and drop cached ids
- Route block-specific RPCs through the owning tab's TabRpcClient
- Never persist blockIds across workspace restores without revalidation
When it happens
Trigger: handle_captureblockscreenshot called with a blockId that was closed, belongs to a different tab/window, was never created, or whose id string is malformed.
Common situations: Client caches blockIds and screenshots a block the user already closed; a UUID from another tab (blockIds are per-tab layout in this call path); typos or stale ids persisted in app state across reloads.
Related errors
- Layout model not found
- Block not found in tab: ${blockId}
- Display container not found
- rpc command "${msg.command}" not supported by [${this.routeI
- Invalid node
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/76931e694c5eb93b.
Report an issue: GitHub.