block/buzz · error · Error

Audio fetch failed (${response.status})

Error message

Audio fetch failed (${response.status})

What it means

decodeSamples fetches audio bytes over HTTP and decodes them via AudioContext. If the fetch response status is not ok (non-2xx), it throws `Audio fetch failed (<status>)`. This surfaces HTTP-level failures (404, 403, 500) from the media/attachment URL as a regular Error.

Source

Thrown at desktop/src/features/messages/ui/AudioMessageAttachment.tsx:79

function audioMimeForUrl(url: string): string {
  const pathname = url.split("?", 1)[0]?.toLowerCase() ?? "";
  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([

View on GitHub (pinned to dad5a33865)

Solutions

  1. Verify the audio URL is valid and the blob still exists on the media server
  2. Check the media endpoint's auth/signing requirements — refresh the URL if it carries an expiring signature
  3. Confirm the relay/Blossom service is running and reachable; retry the fetch
  4. Catch this error in load() and render a 'playback unavailable' state for the attachment

Example fix

// before
const response = await fetch(url, { signal });
if (!response.ok) throw new Error(`Audio fetch failed (${response.status})`);
// after
const response = await fetch(url, { signal });
if (!response.ok) {
  if (response.status >= 500 && retriesLeft > 0) return decodeSamples(url, signal, retriesLeft - 1);
  throw new Error(`Audio fetch failed (${response.status})`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: HEAD the URL before decoding
const head = await fetch(url, { method: 'HEAD' });
if (!head.ok) throw new Error(`Audio unavailable (${head.status})`);

Type guard

null

Try / catch

try {
  const samples = await decodeSamples(url, signal);
} catch (e) {
  if ((e as DOMException).name === 'AbortError') return;
  showPlaybackUnavailable((e as Error).message); // includes status code
}

Prevention

When it happens

Trigger: fetch() of an audio attachment URL returns 404 (attachment deleted), 403 (auth/expired signed URL), or 5xx (relay/media server down). Any non-ok response to the audio blob request inside the component's load path.

Common situations: Blossom media server missing the blob; expired or invalid signed media URL; relay restarted and media endpoint unreachable; CDN serving 502.

Related errors


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