block/buzz · info · DOMException
Audio decode cancelled
Error message
Audio decode cancelled
What it means
After fetching the audio bytes, decodeSamples checks signal.aborted before starting AudioContext decoding and throws AbortError 'Audio decode cancelled'. This prevents wasted CPU decoding audio for a consumer that has already lost interest.
Source
Thrown at desktop/src/features/messages/ui/AudioMessageAttachment.tsx:82
if (pathname.endsWith(".mp4")) return "audio/mp4";
if (pathname.endsWith(".mp3")) return "audio/mpeg";
if (pathname.endsWith(".ogg")) return "audio/ogg";
return "audio/wav";
}
function isAbortError(error: unknown): boolean {
return error instanceof DOMException && error.name === "AbortError";
}
async function decodeSamples(
url: string,
signal: AbortSignal,
): Promise<Float32Array> {
const response = await fetch(url, { signal });
if (!response.ok) throw new Error(`Audio fetch failed (${response.status})`);
const bytes = await response.arrayBuffer();
if (signal.aborted) {
throw new DOMException("Audio decode cancelled", "AbortError");
}
const context = new AudioContext();
let rejectCancellation: ((reason?: unknown) => void) | undefined;
const cancellation = new Promise<never>((_resolve, reject) => {
rejectCancellation = reject;
});
const onAbort = () => {
void context.close().catch(() => undefined);
rejectCancellation?.(
new DOMException("Audio decode cancelled", "AbortError"),
);
};
signal.addEventListener("abort", onAbort, { once: true });
try {
const buffer = await Promise.race([
context.decodeAudioData(bytes),
cancellation,
]);View on GitHub (pinned to dad5a33865)
Solutions
- Check signal.aborted after each await in the caller and bail early
- Catch AbortError in load() and ignore it — it is expected cancellation, not failure
- Avoid recreating attachments rapidly (stabilize list item identity) so aborts fire less often
Example fix
// before
const bytes = await response.arrayBuffer();
// (abort here silently reached decode)
// after
const bytes = await response.arrayBuffer();
if (signal.aborted) throw new DOMException("Audio decode cancelled", "AbortError"); Defensive patterns
Strategy: try-catch
Validate before calling
if (signal.aborted) return; // don't start decode work at all
Type guard
function isAbortError(e: unknown): e is DOMException {
return e instanceof DOMException && e.name === 'AbortError';
} Try / catch
try {
const samples = await decodeSamples(url, signal);
} catch (e) {
if (isAbortError(e)) return; // cancelled, do not render error state
throw e;
} Prevention
- Check signal.aborted after every awaited step in long async chains
- Keep AbortController creation scoped to the mounted component/effect
- Treat post-fetch abort checks as required hygiene in decode pipelines
When it happens
Trigger: The AbortSignal fires (component unmount, message scrolled away, attachment recycled) between response.arrayBuffer() completing and the decode step — i.e. abort landed during the arrayBuffer await.
Common situations: User scrolls fast through a voice-note-heavy channel; message list virtualization unmounts the audio attachment mid-download; channel switch aborts pending loads.
Related errors
- Media fetch cancelled
- Cannot encode an empty voice note
- upload cancelled
- Audio fetch failed (${response.status})
- pool initialization cancelled by shutdown
AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05).
Data as JSON: /api/errors/c43fccb263348ecd.
Report an issue: GitHub.