{"record":{"id":"ea31265b973ba6d8","repo":"remotion-dev/remotion","slug":"getaudioduration-is-only-available-in-the-browse","errorCode":null,"errorMessage":"getAudioDuration() is only available in the browser.","messagePattern":"getAudioDuration\\(\\) is only available in the browser\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-utils/src/get-audio-duration-in-seconds.ts","lineNumber":15,"sourceCode":"/* eslint-disable @typescript-eslint/no-use-before-define */\nimport {onMediaError} from './media-tag-error-handling';\nimport {pLimit} from './p-limit';\n\nconst limit = pLimit(3);\n\nconst metadataCache: {[key: string]: number} = {};\n\nconst fn = (src: string): Promise<number> => {\n\tif (metadataCache[src]) {\n\t\treturn Promise.resolve(metadataCache[src]);\n\t}\n\n\tif (typeof document === 'undefined') {\n\t\tthrow new Error('getAudioDuration() is only available in the browser.');\n\t}\n\n\tconst audio = document.createElement('audio');\n\taudio.src = src;\n\treturn new Promise<number>((resolve, reject) => {\n\t\tconst onError = () => {\n\t\t\tonMediaError({\n\t\t\t\terror: audio.error!,\n\t\t\t\tsrc,\n\t\t\t\tcleanup,\n\t\t\t\treject,\n\t\t\t\tapi: 'getAudioDurationInSeconds()',\n\t\t\t});\n\t\t};\n\n\t\tconst onLoadedMetadata = () => {\n\t\t\tmetadataCache[src] = audio.duration;\n\t\t\tresolve(audio.duration);","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-utils/src/get-audio-duration-in-seconds.ts#L1-L33","documentation":"Thrown by getAudioDurationInSeconds() (and its deprecated alias getAudioDuration) when `document` is undefined (line 14). The implementation creates a real <audio> element via document.createElement to read loadedmetadata, so it cannot run outside a DOM. Note: this API is deprecated in favor of Mediabunny's metadata API.","triggerScenarios":"Calling getAudioDurationInSeconds() during SSR, in a Node script, in Node-based unit tests, or in a Cloudflare/Vercel edge function. Importing the module eagerly and invoking at top-level on the server.","commonSituations":"Server-side pre-render of a Remotion composition; Next.js server component trying to compute clip length; Jest test under Node env without jsdom; build-time data fetching that needs the duration.","solutions":["Call getAudioDurationInSeconds() only from inside Remotion client components or other browser code.","For server-side or test-time duration, migrate to Mediabunny's metadata API which is not DOM-bound (this also removes the deprecation warning).","If you must use this API in tests, set test environment to jsdom or happy-dom and ensure the <audio> element can reach the URL.","Gate the call with typeof document !== 'undefined' to avoid invoking it during SSR."],"exampleFix":"// before (server-side call, throws)\nimport {getAudioDurationInSeconds} from '@remotion/media-utils';\nexport async function loader() {\n  return {duration: await getAudioDurationInSeconds('/a.mp3')};\n}\n\n// after (browser-only via hook)\nimport {useAudioData} from '@remotion/media-utils';\nconst audio = useAudioData(staticFile('a.mp3'));\nconst duration = audio?.durationInSeconds;","handlingStrategy":"validation","validationCode":"import {getAudioDurationInSeconds} from '@remotion/media-utils';\n\nasync function safeGetAudioDurationInSeconds(src: string): Promise<number | null> {\n  if (typeof document === 'undefined') {\n    return null; // not in a browser; caller can fall back to ffprobe/Mediabunny\n  }\n  return getAudioDurationInSeconds(src);\n}","typeGuard":"const canUseDomAudio = (): boolean => typeof document !== 'undefined';","tryCatchPattern":"try {\n  const duration = await getAudioDurationInSeconds(src);\n} catch (err) {\n  if ((err as Error).message.includes('only available in the browser')) {\n    // fall back to a non-DOM path (Mediabunny metadata or ffprobe)\n  } else {\n    throw err;\n  }\n}","preventionTips":["Migrate new code to Mediabunny's metadata API, which works outside the DOM and removes this deprecation.","Only call this helper inside Remotion client components.","Guard isomorphic call sites with typeof document !== 'undefined'.","In Node tests, configure jsdom/happy-dom or skip the test rather than calling into a missing DOM."],"tags":["browser-only","ssr","dom","audio","environment","deprecated"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}