Skip to content

amyboard: report full sketch traceback (line numbers) to web editor#1059

Merged
bwhitman merged 1 commit into
mainfrom
claude/amyboard-sketch-error-traceback
Jun 30, 2026
Merged

amyboard: report full sketch traceback (line numbers) to web editor#1059
bwhitman merged 1 commit into
mainfrom
claude/amyboard-sketch-error-traceback

Conversation

@bwhitman

Copy link
Copy Markdown
Collaborator

What

When an AMYboard sketch fails to load, the device now reports the full Python traceback (with file/line numbers) back to the web editor instead of just the one-line <ExcType>: <message>.

Why

The traceback was already being generated by sys.print_exception() — it just went to the serial console only and never reached the website. The 'X' sysex error frame carried only type(e).__name__ + ": " + str(e).

Note: simulate mode (run-in-browser) already shows the full traceback today, because the web scrapes sys.print_exception output off the JS console (spss.js:17). This gap only affected control mode on real hardware, where stderr goes to the serial port, not the browser — which is exactly the path a user on real hardware hits.

How

  • New _format_exc_for_report(e) helper in tulip/shared/amyboard-py/amyboard.py captures sys.print_exception(e, buf) into an io.StringIO and returns the string.
  • The three sketch-load error call sites now send _format_exc_for_report(e) instead of the one-liner.
  • Single sysex frame, no chunking. Real MicroPython tracebacks are ~150–300 bytes; a single frame reliably carries ~768 raw bytes over Web MIDI (ZDUMP_STREAM_RAW_CHUNK in amy/src/transfer.c). The helper bounds output to 700 bytes, keeping the tail (MicroPython prints most-recent-call-last, so the exception line is last) and prepending a ...(traceback truncated) marker if it ever trims a pathologically deep stack.
  • Falls back to the old type: message one-liner if traceback capture ever fails — best-effort, never masks the original error or breaks the self-heal.
  • No web-side change: show_python_error already renders multi-line text in a monospace <pre> with white-space:pre-wrap.

Deliberately out of scope

The sketch.loop() runtime-error path is left serial-only. It runs every ~60ms, so reporting from there would spam sysex frames / re-pop the modal every tick. The existing feature (and this PR) covers the load path, which fires once.

Testing

  • Truncation logic unit-tested in isolation (short tracebacks pass through whole; deep ones truncate from the front but preserve the exception line; stays under the 1024 base64-char single-frame limit).
  • Needs hardware verification on the Pi HW-CI bench: flash AMYboard firmware, push a sketch with a deliberate error (e.g. NameError on a known line), and confirm the web editor modal shows the full traceback with correct line N (not line 0 — confirms MICROPY_ENABLE_SOURCE_LINE is compiled in).

🤖 Generated with Claude Code

When a sketch fails to load, run_sketch() reported only
"<ExcType>: <message>" back to the web editor over the 'X' sysex frame —
the full traceback (with file/line numbers) was generated by
sys.print_exception() but only went to the serial console, never the
website. In simulate mode the web already shows the full traceback (it
scrapes it off the JS console), so this gap only affected control mode
on real hardware.

Capture sys.print_exception() into an io.StringIO and report that
instead, via a new _format_exc_for_report() helper. Kept as a single
sysex frame (no chunking): real MicroPython tracebacks are ~150-300
bytes and a single frame reliably carries ~768 raw bytes over Web MIDI
(ZDUMP_STREAM_RAW_CHUNK in amy/src/transfer.c). The helper bounds output
to 700 bytes, keeping the tail (MicroPython prints most-recent-call-last,
so the exception line is last) and falling back to the old one-liner if
capture ever fails. The web display already renders multi-line text, so
no web-side change is needed.

The sketch.loop() runtime-error path is intentionally left serial-only:
it runs every ~60ms and reporting from there would spam sysex frames.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔌 AMYboard PR preview

Editor + flasher: https://amyboard-pr-1059.vercel.app/editor/

This preview bundles this PR's firmware — its flasher only flashes this build (not the release). Rebuilt on every push; removed when the PR closes.

The hardware CI has been kicked off and should return within a few minutes, stand by!

@github-actions

Copy link
Copy Markdown

🌷 Tulip Web PR preview

Tulip Web: https://tulip-pr-1059.vercel.app/run/

Flash a Tulip to this build: on-device run tulip.upgrade(pr=1059).

Rebuilt on every push; removed when the PR closes.

@github-actions

Copy link
Copy Markdown

🎛️ HW CI (physical bench)

AMYboard (USB-MIDI + AMY zP → audio): ✅ PASS — flashed this PR’s firmware; all checks matched the references.

Tulip (TULIP4_R11; serial-REPL audio + WiFi screenshot): ✅ PASS — flashed this PR’s firmware; all checks matched the references.

⬇️ Artifacts: recordings · screenshot · serial logs · run logs

Self-hosted bench. Audio spectral-compared to ref/hwci_basic.wav + ref/tulip_basic.wav; Tulip screenshot pixel-compared to ref/tulip_screenshot.png. Both analog outs share one capture card, so the tests run sequentially.

@bwhitman bwhitman merged commit 4f32453 into main Jun 30, 2026
3 checks passed
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.

1 participant