fix: reject with proper Error on audio error event (fixes #339115) - #339121
VS Code PR Bot (vscodebot-pr) wants to merge 3 commits into
Conversation
…39115) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The functional fix is correct; only a minor comment-format issue remains.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Fixes accessibility signal failures rejecting with undefined, preventing a secondary TypeError.
Changes:
- Rejects audio errors with a proper
Error. - Includes available
MediaErrordiagnostics.
| File | Description |
|---|---|
src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts |
Corrects audio error-event rejection handling. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // When the error event fires, ended might not be called. | ||
| // The media `error` event is a plain Event without an `error` | ||
| // property; the actual failure is described by `audio.error`. |
The Compile & Hygiene job repeatedly killed ESLint with exit code 137 while core-ci was bundling. Run the full linter after the parallel checks to avoid overlapping their memory peaks without skipping validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore the original Code OSS workflow in this PR. Track the shared ESLint resource-contention fix independently in microsoft#339153. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the resource-contention mitigation in this standalone CI PR, separate from the accessibility fix in #339121. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Separated the infrastructure fix from this accessibility PR as requested. The shared workflow is restored byte-for-byte, and this PR's net diff now contains only This is not specific to the audio change: the same ESLint exit-137 signature was verified on #335978, #339105, and #339110. The latter two subsequently passed and merged. Fresh CI is running against this PR's restored accessibility-only head. No PR has been merged as part of this work.
|

Summary
An accessibility signal sound fails to play, and the
errorevent handler indoPlayAudiorejects the promise withe.error— but a DOM mediaerrorevent is a plainEventthat has noerrorproperty, so the promise rejects withundefined. Thatundefinedthen reaches thecatch (e)block inplaySound, which readse.messageand throwsTypeError: Cannot read properties of undefined (reading 'message'). Impact: an unhandled error is logged to telemetry whenever an accessibility cue sound fails to load/play (e.g. missing/blocked media), affecting 425 users on stable 1.140.0.Fixes #339115
Recommended reviewer:
@meganroggeCulprit Commit
reject(e.error)has existed since the file was created; no recent commit changed the triggering logic. This bucket is new on the 1.140.0 stable channel but the defect is long-standing (surfaced at stable scale / under conditions where a cue sound fails to play). This is best characterized as a pre-existing bug newly observed, not a code regression.Code Flow
sequenceDiagram participant Caller as playSignal participant Sound as playSound participant Audio as doPlayAudio participant DOM as HTMLAudioElement Caller->>Sound: await playSound(sound) Sound->>Audio: await playAudio(url, volume) Audio->>DOM: new Audio(url)#59; audio.play() DOM-->>Audio: 'error' event (plain Event, no .error) Note over Audio: ⚠️ Root cause (L297):<br/>reject(e.error) → rejects with undefined Audio-->>Sound: promise rejects with undefined Note over Sound: 💥 L211: e.message<br/>Cannot read properties of<br/>undefined (reading 'message')Affected Files
src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.tsreject(e.error)— the mediaerrorevent is a plainEventwith no.errorproperty, so the promise rejects withundefinedsrc/vs/platform/accessibilitySignal/browser/accessibilitySignalService.tsif (!e.message.includes(...))reads.messageon theundefinedrejection valueRepro Steps
accessibility.signalOptions, then perform an action that plays a cue).errorevent — e.g. the media resource fails to load, is blocked, or is otherwise unplayable (the browser/Electron fireserrorrather than resolvingplay()).errorhandler rejects the play promise withundefined; thecatch (e)inplaySoundreadse.messageand throwsTypeError: Cannot read properties of undefined (reading 'message'), which is reported as an unhandled error.Because
play()'s own rejection path (L300) rejects with a realError, the crash only manifests via the mediaerrorevent path, which explains the intermittent, environment-dependent occurrence.How the Fix Works
Chosen approach (
accessibilitySignalService.ts→doPlayAudio): fix the data producer, not the crash site. Theerror-event handler no longer rejects with the non-existente.error. Instead it constructs a properError, enriched with the real failure details fromaudio.error(anHTMLMediaElement'sMediaError, carrying.codeand.message) when available, and a generic"Failed to play audio"message otherwise. After this change the promise always rejects with a genuineErrorinstance, so the downstreamcatch (e)inplaySoundcan always reade.messagesafely — theTypeErrorcan no longer be produced.This keeps the existing
logService/console.errortelemetry path intact (thecatchblock still runs and still logs genuine failures), and it enriches the error with cross-boundary diagnostic context (MediaError.code) rather than swallowing it.Alternatives considered:
e?.messageorif (!e) returninplaySound's catch) — rejected: that is a crash-site guard that masks the producer bug, leaves every other consumer ofplayAudioreceiving a meaninglessundefinedrejection, and discards the real failure cause.Recommended Owner
@meganrogge— area owner ofaccessibilitySignalService.ts; authored the large majority of this file's history and is highly active inmicrosoft/vscode(hundreds of commits in the last 90 days). The culprit is pre-existing, so ownership falls to the feature-area owner.