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
- Verify the audio URL is valid and the blob still exists on the media server
- Check the media endpoint's auth/signing requirements — refresh the URL if it carries an expiring signature
- Confirm the relay/Blossom service is running and reachable; retry the fetch
- 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
- Render a fallback UI when audio bytes are unavailable
- Refresh expiring signed media URLs before fetching
- Monitor media server 5xx rates to catch backend issues early
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
- mesh endpoint bind on {} failed: {e}
- Failed to bind health port {}: {e}
- Failed to bind {}: {e}
- TCP server error: {e}
- Server error: {e}
AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05).
Data as JSON: /api/errors/0995ef0e98f97436.
Report an issue: GitHub.