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

  1. Check signal.aborted after each await in the caller and bail early
  2. Catch AbortError in load() and ignore it — it is expected cancellation, not failure
  3. 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

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


AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05). Data as JSON: /api/errors/c43fccb263348ecd. Report an issue: GitHub.