claude-code - 💡(How to fix) Fix [BUG] preview_screenshot silently hangs the session when Preview server is not found — error is logged but never returned to tool caller

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When preview_screenshot is called with a serverId that no longer exists (evicted, idle-killed, or belonging to a different concurrent session), the internal PreviewError is logged as [warn] in main.log but never propagated back to the MCP tool caller. The Claude session hangs indefinitely on the spinner with no error, no timeout, and no recovery path.


Error Message

2026-05-07 21:13:24 [warn] [Preview] capturePreviewScreenshot failed: { error: TO [PreviewError]: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2 at Object.capturePreviewScreenshot (.../app.asar/.vite/build/index.js:1264:17074) [message]: 'Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2', name: 'PreviewError'

Root Cause

capturePreviewScreenshot throws a PreviewError when the serverID is not in the active registry. The error is caught and logged at [warn] level, but the MCP tool response is never sent back to the calling Claude session. The tool call is left open, blocking the session's tool-use pipeline indefinitely.

This is consistent with the behavior described in #57134 (retry-budget exhaustion causing silent hangs) — the same pattern of "error is known internally but never surfaced to the session."


Code Example

2026-05-07 20:41:45 [info] [Preview] Starting server with config
2026-05-07 20:41:45 [info] [Preview] Process spawned { pid: 60693 }
2026-05-07 20:41:49 [info] [Preview] Server ready (HTTP responding) { serverId: '95c45eea-105f-4253-b737-52d6c014e1b2' }

---

2026-05-07 21:13:24 [warn] [Preview] capturePreviewScreenshot failed: {
  error: TO [PreviewError]: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2
      at Object.capturePreviewScreenshot (.../app.asar/.vite/build/index.js:1264:17074)
    [message]: 'Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2',
    name: 'PreviewError'

---

2026-05-06 23:17:18 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-06 23:22:01 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-06 23:23:43 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-07 13:19:28 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-07 19:24:30 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2
2026-05-07 20:07:29 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2
2026-05-07 21:13:24 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2

---

- } catch (e) {
-   logger.warn('[Preview] capturePreviewScreenshot failed:', { error: e });
- }
+ } catch (e) {
+   logger.warn('[Preview] capturePreviewScreenshot failed:', { error: e });
+   return { isError: true, content: [{ type: 'text', text: e.message }] };
+ }
RAW_BUFFERClick to expand / collapse

[BUG] preview_screenshot silently hangs the session when Preview server is not found — error is logged but never returned to the tool caller

Summary

When preview_screenshot is called with a serverId that no longer exists (evicted, idle-killed, or belonging to a different concurrent session), the internal PreviewError is logged as [warn] in main.log but never propagated back to the MCP tool caller. The Claude session hangs indefinitely on the spinner with no error, no timeout, and no recovery path.


Reproduction

  1. Open 2+ Claude Code sessions simultaneously, each working on a web project with Claude Preview active.
  2. In any session, trigger preview_screenshot after the preview server has been idle or after a Claude Desktop restart.
  3. Expected: tool returns an error — e.g. {"error": "Preview not found for server <id>"} — and the session continues.
  4. Actual: session freezes indefinitely. No error shown. Spinner never resolves.

Reliably reproducible with parallel sessions or after the IdleManager:preview evicts a server (1800s timeout).


Evidence — logs from ~/.config/Claude/logs/main.log

Preview server spawned and marked ready:

2026-05-07 20:41:45 [info] [Preview] Starting server with config
2026-05-07 20:41:45 [info] [Preview] Process spawned { pid: 60693 }
2026-05-07 20:41:49 [info] [Preview] Server ready (HTTP responding) { serverId: '95c45eea-105f-4253-b737-52d6c014e1b2' }

Same session, 31 minutes later — screenshot fails silently:

2026-05-07 21:13:24 [warn] [Preview] capturePreviewScreenshot failed: {
  error: TO [PreviewError]: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2
      at Object.capturePreviewScreenshot (.../app.asar/.vite/build/index.js:1264:17074)
    [message]: 'Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2',
    name: 'PreviewError'

The error recurs across days and multiple server IDs — this is not a one-off:

2026-05-06 23:17:18 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-06 23:22:01 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-06 23:23:43 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-07 13:19:28 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server ba4f127d-dcbf-4d83-a295-b5a62a4e8d45
2026-05-07 19:24:30 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2
2026-05-07 20:07:29 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2
2026-05-07 21:13:24 [warn] [Preview] capturePreviewScreenshot failed: Preview not found for server b50c9380-9bbd-4025-898a-ba1597eae9b2

Note: b50c9380 is a different serverID than 95c45eea (the one spawned for that session at 20:41:49). This indicates Claude is sometimes calling preview_screenshot with a stale serverID from a previous or concurrent session — and getting no feedback when it fails.


Root cause

capturePreviewScreenshot throws a PreviewError when the serverID is not in the active registry. The error is caught and logged at [warn] level, but the MCP tool response is never sent back to the calling Claude session. The tool call is left open, blocking the session's tool-use pipeline indefinitely.

This is consistent with the behavior described in #57134 (retry-budget exhaustion causing silent hangs) — the same pattern of "error is known internally but never surfaced to the session."


Impact

  • Every session that calls preview_screenshot after an idle eviction or with a stale serverID is permanently frozen.
  • With 2+ parallel sessions, stale serverID cross-contamination is common — both sessions seen frozen in under 1 hour of normal use (today's session: 20:28 and 21:08 hang timestamps).
  • No user-facing recovery: Escape does not unblock the tool call. Session must be closed and restarted, losing context.

Proposed fix

In the capturePreviewScreenshot handler, catch PreviewError and return it as a structured MCP tool error instead of logging and silently dropping it:

- } catch (e) {
-   logger.warn('[Preview] capturePreviewScreenshot failed:', { error: e });
- }
+ } catch (e) {
+   logger.warn('[Preview] capturePreviewScreenshot failed:', { error: e });
+   return { isError: true, content: [{ type: 'text', text: e.message }] };
+ }

This single change would let Claude recover gracefully — it would see the error, understand the preview server is gone, and could call preview_start to respawn it rather than hanging forever.


Environment

  • Claude Desktop 0.10.x / Claude Code 2.1.64
  • Linux (Fedora 43, kernel 6.19)
  • Multiple concurrent Claude Code sessions (3–5) on Max 20x plan
  • Preview idle timeout: 1800s (IdleManager:preview)

Related

  • #57134 — client-side silent hang pattern (same failure mode, different trigger)
  • #53922 — concurrent session rate-limiting (parallel context)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING