Skip to content

Absorb the AbortError that priming the voice clip provokes - #158

Merged
Knorcedger merged 1 commit into
mainfrom
claude/pause-menu-autosave-33m17i
Aug 23, 2026
Merged

Absorb the AbortError that priming the voice clip provokes#158
Knorcedger merged 1 commit into
mainfrom
claude/pause-menu-autosave-33m17i

Conversation

@Knorcedger

Copy link
Copy Markdown
Owner

What changed?

The first pointer press of every match logged an uncaught error to the console:

Uncaught (in promise) AbortError: The play() request was interrupted by a call to pause().

createRecordedSpeech(...).prime() spends the browser's first user gesture on the recorded-voice
clip so a later beat is allowed to play: it sets a silent src, calls play(), and immediately
pauses. That pause is the whole point, and it rejects the play() it interrupts — but
void target.play() discarded the promise, so an expected rejection surfaced as an unhandled one.

Attaching a .catch keeps the behaviour identical and takes the error out of the console. speak()
already handles its own play() promise and cancel()/stopClip() are covered by the generation
guard, so prime() was the only unhandled path; a genuine autoplay refusal is still acted on where
it matters.

Related issue

Closes #156

Why this approach?

Nothing was actually broken — priming worked and the silence was intended. The case for fixing it is
that the error is alarming, fires on the first press of every match, and lands in the middle of the
event sequence anyone debugging input is reading. It cost real time during #153 by appearing to be
part of a dead pause button.

Suppressing the rejection at its source is preferable to a global unhandledrejection filter, which
would hide genuine audio failures too.

Verification

Driven in a real browser: three pause presses across a match, including the first gesture.

Console output
Before AbortError: The play() request was interrupted by a call to pause() on the first press
After empty — zero errors

The new test was confirmed to fail against the old prime() and pass against the new one, so it
genuinely guards the regression.

  • npm run typecheck
  • npm test — 1401 passed, 11 skipped
  • npm run build, npm run bundle:check, npm run bundle:self-test
  • I added or updated tests where behavior changed — a prime() whose play() rejects produces
    no unhandled rejection, and still plays the silent clip, pauses it, and primes the fallback
  • I included screenshots or a recording for visible changes — no visible change; the table above
    is the evidence

Provenance and AI assistance

Claude Code (Claude Opus) diagnosed, fixed, and tested this. No generated assets are included.

Contributor checklist

  • I searched issues and active pull requests before implementation and coordinated ownership in the linked issue
  • This pull request closes or references exactly one primary coordination issue
  • I read CONTRIBUTING.md and followed the deterministic simulation rules
  • I reviewed and understand every submitted change
  • I have the right to submit this code and any included assets
  • My commits are signed off under the Developer Certificate of Origin (git commit -s)
  • This PR contains no credentials, private player data, or unlicensed third-party material

🤖 Generated with Claude Code

https://claude.ai/code/session_01BoQrnJcCawJvZ9jGHx5W4P


Generated by Claude Code

The first pointer press of every match logged an uncaught error:

  AbortError: The play() request was interrupted by a call to pause().

createRecordedSpeech's prime() spends the browser's first user gesture on the
recorded-voice clip so a later beat is allowed to play. It sets a silent src,
calls play(), and immediately pauses. That pause is the point of the exercise
and it rejects the play it interrupts — but `void target.play()` discarded the
promise, so an expected rejection surfaced as an unhandled one.

Nothing was broken: priming worked and the silence was intended. The error was
alarming, fired on the first press of every match, and landed in the middle of
the event sequence anyone debugging input is reading — it cost real time during
the pause-button investigation by looking like part of a dead button.

Attach a catch, keeping the behaviour identical. speak() already handles its own
play() promise and cancel()/stopClip() are covered by the generation guard, so
prime() was the only unhandled path; a genuine autoplay refusal is still acted
on where it matters.

Verified in a browser: three presses across a match now produce an empty console
where the first press previously logged the AbortError. The new test fails
against the old prime().

Closes #156

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BoQrnJcCawJvZ9jGHx5W4P
Signed-off-by: Claude <knorcedger@gmail.com>
@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
bannerfall Ready Ready Preview Aug 23, 2026 9:40am

Request Review

@Knorcedger
Knorcedger merged commit cfd668a into main Aug 23, 2026
3 checks passed
@Knorcedger
Knorcedger deleted the claude/pause-menu-autosave-33m17i branch August 23, 2026 09:41
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.

Priming the voice-over clip logs an uncaught AbortError on the first press of every match

1 participant