Skip to content

Report debug script syntax errors as the run's error, immediately - #38

Open
simonw wants to merge 7 commits into
mainfrom
claude/debug-tool-script-error-0mo2o3
Open

simonw wants to merge 7 commits into
mainfrom
claude/debug-tool-script-error-0mo2o3

Conversation

@simonw

@simonw simonw commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

A debug script that failed to compile never ran its wrapper, so the run
waited out its whole timeout and the only clue was an app-level
javascript-error event - which WebKit sanitizes in sandboxed frames to
"Script error. (details withheld ...)".

The bridge now checks whether the injected script started. If it did
not, the error event raised during insertion becomes the run's result:
the parser message plus line/column within the debug script on
Chromium and Firefox, or an explanation that details were withheld on
WebKit. The run finishes straight away instead of timing out.

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85

A debug script that failed to compile never ran its wrapper, so the run
waited out its whole timeout and the only clue was an app-level
javascript-error event - which WebKit sanitizes in sandboxed frames to
"Script error. (details withheld ...)".

The bridge now checks whether the injected script started. If it did
not, the error event raised during insertion becomes the run's result:
the parser message plus line/column within the debug script on
Chromium and Firefox, or an explanation that details were withheld on
WebKit. The run finishes straight away instead of timing out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85
Newer Datasette releases add "private" to responses for signed-in
actors. Check for the no-store directive instead of the exact header so
the test passes against older and newer Datasette alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85
…rapping

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85
WebKit reports every uncaught error from a classic script in a
sandboxed (opaque origin) frame as a bare "Script error.", so app_debug
runs in Safari told the agent nothing about the app's own errors.
Probing WebKit in CI showed module scripts and same-origin external
scripts keep their error details, and that a MutationObserver sees each
parser-inserted script before it runs.

On engines where a probe error comes back sanitized, the debug bridge
now:

- swaps each inline classic <script>, just before it runs, for a
  same-text blob: script - still a classic script run in document order,
  so globals, document.write and document.currentScript are unchanged.
  Its errors are reported against "app-script-N" (the app's Nth
  <script> element) with the failing line as sourceLine
- wraps callbacks the app registers (timers, animation frames,
  microtasks, listeners, on* handler properties, inline on* attributes,
  observers) to capture what they throw before it is sanitized
- names the script whose top-level code threw when neither applies

The debug frame CSP additionally allows blob: scripts (granting nothing
'unsafe-inline' does not) and sets worker-src 'none' to keep workers
blocked. The production app view policy is unchanged. Chromium and
Firefox report full details already, so none of this activates there.

Debug scripts now run after DOMContentLoaded, so they always see the
page after its own parser-inserted scripts have run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85
WebKit drains microtasks between a callback invoked from the event loop
(a timer, a real user click) throwing and reporting the exception, so a
remembered error cleared by a microtask was gone before its sanitized
"Script error." event arrived. Remember caught errors on a stack cleared
by a later task instead - the report always lands within the same task,
and a callback throwing during that microtask drain is reported first.

The recovery test now reports every captured error in full on failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VX4speFtsjrJCtLtVaFB85

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants