{"record":{"id":"130243da1470de28","repo":"remotion-dev/remotion","slug":"failed-to-fetch-audio-data-from-src-response","errorCode":null,"errorMessage":"Failed to fetch audio data from ${src}: ${response.status} ${response.statusText}","messagePattern":"Failed to fetch audio data from (.+?): (.+?) (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-utils/src/get-audio-data.ts","lineNumber":33,"sourceCode":"const fn = async (\n\tsrc: string,\n\toptions?: Options,\n): Promise<MediaUtilsAudioData> => {\n\tif (metadataCache[src]) {\n\t\treturn metadataCache[src];\n\t}\n\n\tif (typeof document === 'undefined') {\n\t\tthrow new Error('getAudioData() is only available in the browser.');\n\t}\n\n\tconst audioContext = new AudioContext({\n\t\tsampleRate: options?.sampleRate ?? 48000,\n\t});\n\n\tconst response = await fetchWithCorsCatch(src, options?.requestInit);\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Failed to fetch audio data from ${src}: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tconst arrayBuffer = await response.arrayBuffer();\n\n\tconst wave = await audioContext.decodeAudioData(arrayBuffer);\n\n\tconst channelWaveforms = new Array(wave.numberOfChannels)\n\t\t.fill(true)\n\t\t.map((_, channel) => {\n\t\t\treturn wave.getChannelData(channel);\n\t\t});\n\n\tconst metadata: MediaUtilsAudioData = {\n\t\tchannelWaveforms,\n\t\tsampleRate: wave.sampleRate,\n\t\tdurationInSeconds: wave.duration,","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-utils/src/get-audio-data.ts#L15-L51","documentation":"Thrown by getAudioData() when the underlying fetch() of the audio src returns a non-2xx response (line 32-35). The message embeds the request URL, HTTP status, and statusText so you can see exactly what the server rejected. It is a hard failure: the function cannot decode audio data it never received.","triggerScenarios":"Passing a 404 audio URL; an expired/expired-signed S3 or CloudFront URL; a relative path that 404s under Studio because staticFile() was not used; CORS preflight returning 403; a remote URL behind auth without credentials; calling before the dev/static server is up.","commonSituations":"Hard-coded URL that works locally but 404s in production; forgetting staticFile() and passing a bare '/public/audio.mp3' that Studio can't resolve; CDN/signed URL expired between fetch and decode; remote audio behind Cloudflare hotlink protection returning 403; wrong file extension casing on case-sensitive hosts.","solutions":["Open the exact URL printed in the message in a browser/Postman and confirm it returns 200 with an audio MIME type.","For local assets in Remotion, pass staticFile('audio.mp3') and place the file under public/, never a raw '/public/...' path.","For remote assets, verify the signed URL has not expired and re-issue it; pass {requestInit: {credentials: 'include'}} if cookies/auth are required.","Check server CORS headers: the response needs Access-Control-Allow-Origin matching your origin (or use a CORS proxy). The fetchWithCorsCatch wrapper usually throws a different CORS message first, so a plain status error means the host itself rejected the request.","Confirm the asset host is reachable from the browser tab running Remotion (firewall, VPN, localhost vs 0.0.0.0)."],"exampleFix":"// before (relative path Studio cannot resolve -> 404)\nconst data = await getAudioData('/public/song.mp3');\n\n// after (resolve through Remotion's static file server)\nimport {staticFile} from 'remotion';\nconst data = await getAudioData(staticFile('song.mp3'));","handlingStrategy":"try-catch","validationCode":"// Verify the URL is reachable with the expected status before decoding\nasync function assertAudioReachable(src: string, requestInit?: RequestInit) {\n  const res = await fetch(src, {method: 'GET', ...requestInit});\n  if (!res.ok) {\n    throw new Error(`Audio URL ${src} returned ${res.status} ${res.statusText}`);\n  }\n  const type = res.headers.get('content-type') ?? '';\n  if (!type.startsWith('audio/') && !type.startsWith('video/') && type !== 'application/octet-stream') {\n    console.warn(`Unexpected content-type ${type} for ${src}`);\n  }\n}","typeGuard":"const isHttpOk = (status: number): boolean => status >= 200 && status < 300;","tryCatchPattern":"try {\n  const data = await getAudioData(src, {requestInit});\n} catch (err) {\n  const msg = (err as Error).message;\n  if (msg.startsWith('Failed to fetch audio data')) {\n    // surface the URL/status, log, and decide on a fallback (skip visualization)\n    console.error('Audio fetch failed:', msg);\n    return null;\n  }\n  throw err;\n}","preventionTips":["For local assets always use staticFile() so Remotion serves the correct URL.","For remote/signed URLs, refresh the signature close to use time; do not cache it long-term.","Add a quick HEAD/GET check in dev to confirm the URL returns 2xx and an audio MIME type.","Make sure CORS headers on the host allow your Studio origin before relying on cross-origin fetch.","Use absolute https URLs in production; avoid protocol-relative or hardcoded http URLs behind https pages."],"tags":["network","http","fetch","audio","cors","staticfile"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}