{"record":{"id":"00e760286f317e13","repo":"remotion-dev/remotion","slug":"failed-to-render-gif-with-source-src-error","errorCode":null,"errorMessage":"Failed to render GIF with source ${src}: \"${error.message}\". You must enable CORS for this URL. Open the Developer Tools to see exactly why this fetch failed.","messagePattern":"Failed to render GIF with source (.+?): \"(.+?)\"\\. You must enable CORS for this URL\\. Open the Developer Tools to see exactly why this fetch failed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/gif/src/GifForDevelopment.tsx","lineNumber":110,"sourceCode":"\t\t\t\t\tif (currentOnError.current) {\n\t\t\t\t\t\tcurrentOnError.current(err);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetError(err);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tif (!done) {\n\t\t\t\t\taborted = true;\n\t\t\t\t\tcancel();\n\t\t\t\t}\n\t\t\t};\n\t\t}, [cacheKey, resolvedSrc]);\n\n\t\tif (error) {\n\t\t\tconsole.error(error.stack);\n\t\t\tif (isCorsError(error)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to render GIF with source ${src}: \"${error.message}\". You must enable CORS for this URL. Open the Developer Tools to see exactly why this fetch failed.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to render GIF with source ${src}: \"${error.message}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst index = useCurrentGifIndex({\n\t\t\tdelays: state.delays,\n\t\t\tloopBehavior,\n\t\t\tplaybackRate,\n\t\t});\n\n\t\tif (index === -1) {\n\t\t\treturn null;\n\t\t}","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/gif/src/GifForDevelopment.tsx#L92-L128","documentation":"Thrown by the @remotion/gif <Gif> component (the GifForDevelopment variant used inside Remotion Studio) when the GIF at `src` cannot be fetched because the remote host blocked the request via CORS policy. The component first runs `isCorsError(error)` and, if true, re-throws with this CORS-specific guidance. It is a hard failure: the React render throws and the Studio preview breaks.","triggerScenarios":"Rendering `<Gif src=\"https://cdn.example.com/anim.gif\" />` in the Remotion Studio preview where `cdn.example.com` does not return an `Access-Control-Allow-Origin` header that matches the Studio origin (e.g. localhost:3000). Also triggered when the host returns CORS headers for a different origin, or when credentials are sent but `Access-Control-Allow-Credentials` is missing.","commonSituations":"Pointing `src` at a production S3/Cloudflare bucket that has not been configured for cross-origin requests; hotlinking a GIF from a third-party site that actively blocks CORS; migrating assets but forgetting to enable CORS on the new bucket; using an http:// GIF from an https:// Studio (mixed content that surfaces as a fetch failure reported as CORS).","solutions":["Move the GIF into your Remotion project's `public/` folder and reference it via `staticFile('anim.gif')` so it is served same-origin.","Configure the hosting bucket/CDN to send `Access-Control-Allow-Origin: *` (or your specific Studio origin) for GIF responses.","Open the browser Developer Tools Network tab, select the failing GIF request, and confirm the missing/incorrect CORS header before fixing the server.","If you cannot control the remote host, download the GIF once and serve it locally instead of hotlinking."],"exampleFix":"// before\n<Gif src=\"https://third-party-cdn.com/anim.gif\" />\n\n// after\n<Gif src={staticFile('anim.gif')} />","handlingStrategy":"fallback","validationCode":"import {staticFile} from 'remotion';\n\n// Prefer same-origin assets to avoid CORS entirely.\nconst gifSrc = isRemoteUrl(src) && !hasCorsSupport(src)\n  ? staticFile('anim.gif')\n  : src;","typeGuard":null,"tryCatchPattern":"// <Gif> throws during render; wrap an ErrorBoundary to keep Studio usable.\nimport {Component, type ReactNode} from 'react';\n\nclass GifBoundary extends Component<{children: ReactNode; fallback: ReactNode}, {hasError: boolean}> {\n  state = {hasError: false};\n  static getDerivedStateFromError() { return {hasError: true}; }\n  render() { return this.state.hasError ? this.props.fallback : this.props.children; }\n}\n\n// <GifBoundary fallback={<Img src={placeholder} />}><Gif src={src} /></GifBoundary>","preventionTips":["Default to staticFile() or a bundled import for GIFs instead of remote URLs.","If you must use a remote URL, confirm the host sends Access-Control-Allow-Origin for your Studio origin before wiring it into a composition.","Add an ErrorBoundary around <Gif> so one bad asset does not blank the whole Studio preview."],"tags":["gif","cors","network","studio","fetch"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}