{"record":{"id":"9d4c7f8653484d56","repo":"wavetermdev/waveterm","slug":"failed-to-capture-screenshot-w","errorCode":null,"errorMessage":"failed to capture screenshot: %w","messagePattern":"failed to capture screenshot: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/tools_screenshot.go","lineNumber":45,"sourceCode":"\t\t\treturn \"\", fmt.Errorf(\"missing or invalid widget_id parameter\")\n\t\t}\n\n\t\tctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancelFn()\n\n\t\tfullBlockId, err := wcore.ResolveBlockIdFromPrefix(ctx, tabId, blockIdPrefix)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\trpcClient := wshclient.GetBareRpcClient()\n\t\tscreenshotData, err := wshclient.CaptureBlockScreenshotCommand(\n\t\t\trpcClient,\n\t\t\twshrpc.CommandCaptureBlockScreenshotData{BlockId: fullBlockId},\n\t\t\t&wshrpc.RpcOpts{Route: wshutil.MakeTabRouteId(tabId)},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to capture screenshot: %w\", err)\n\t\t}\n\n\t\treturn screenshotData, nil\n\t}\n}\n\nfunc GetCaptureScreenshotToolDefinition(tabId string) uctypes.ToolDefinition {\n\treturn uctypes.ToolDefinition{\n\t\tName:        \"capture_screenshot\",\n\t\tDisplayName: \"Capture Screenshot\",\n\t\tDescription: \"Capture a screenshot of a widget and return it as an image\",\n\t\tToolLogName: \"gen:screenshot\",\n\t\tStrict:      true,\n\t\tInputSchema: map[string]any{\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": map[string]any{\n\t\t\t\t\"widget_id\": map[string]any{\n\t\t\t\t\t\"type\":        \"string\",","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/tools_screenshot.go#L27-L63","documentation":"This error wraps any failure from the wshclient.CaptureBlockScreenshotCommand RPC, which asks the Wave tab's route to render a block (widget) and return screenshot data. It is thrown by the AI capture_screenshot tool when the underlying RPC returns an error — e.g. the tab route is unavailable, the block cannot be resolved/rendered, or the RPC times out (5s context deadline). The original cause is preserved via %w so unwrap it for details.","triggerScenarios":"Calling the capture_screenshot AI tool where CaptureBlockScreenshotCommand over RpcOpts{Route: MakeTabRouteId(tabId)} fails: tab route not connected, invalid/removed block id, renderer failure, or the 5-second context timeout elapsing before a response.","commonSituations":"AI agent passes a stale widget_id for a block that was closed; the target tab was closed or the wsh route is down; the block contents hang (e.g. a stuck terminal) causing the 5s deadline to expire; running outside the Wave app where no bare RPC client connection exists.","solutions":["Unwrap the error (%v with errors.Unwrap or errors.As) to see the root cause (timeout vs not-found vs route error)","Verify the widget_id/blockId is valid and the block still exists in the given tab","Confirm the tab is open and its wsh route is connected before calling the RPC","Retry once if the failure was a deadline/timeout, or increase the 5s context timeout","Check that the block type actually supports screenshot capture"],"exampleFix":"// before\nscreenshotData, err := wshclient.CaptureBlockScreenshotCommand(rpcClient, wshrpc.CommandCaptureBlockScreenshotData{BlockId: fullBlockId}, &wshrpc.RpcOpts{Route: wshutil.MakeTabRouteId(tabId)})\nif err != nil { return \"\", fmt.Errorf(\"failed to capture screenshot: %w\", err) }\n// after\nscreenshotData, err := wshclient.CaptureBlockScreenshotCommand(rpcClient, wshrpc.CommandCaptureBlockScreenshotData{BlockId: fullBlockId}, &wshrpc.RpcOpts{Route: wshutil.MakeTabRouteId(tabId), TimeoutMs: 10000})\nif err != nil { return \"\", fmt.Errorf(\"failed to capture screenshot for block %s: %w\", fullBlockId, err) }","handlingStrategy":"try-catch","validationCode":"ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\nif _, err := wcore.ResolveBlockIdFromPrefix(ctx, tabId, widgetId); err != nil {\n    return fmt.Errorf(\"invalid widget %s in tab %s: %w\", widgetId, tabId, err)\n}","typeGuard":"func isTimeoutErr(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) && ne.Timeout()\n}","tryCatchPattern":"screenshot, err := captureTool(input)\nif err != nil {\n    if isTimeoutErr(err) {\n        // retry once with longer deadline\n    }\n    log.Printf(\"screenshot capture failed: %v\", err)\n    return fmt.Errorf(\"capture_screenshot unavailable: %w\", err)\n}","preventionTips":["Validate the widget_id resolves to an existing block before capturing","Ensure the target tab is open and its wsh route is connected","Use a retry with backoff for deadline-type failures","Keep the 5s context timeout in mind; avoid capturing blocks that block rendering"],"tags":["rpc","screenshot","wave","timeout"],"backgroundTag":"rpc-call-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}