{"record":{"id":"d1b3b7dea33f249a","repo":"remotion-dev/remotion","slug":"remote-asset-urls-cannot-include-credentials","errorCode":null,"errorMessage":"Remote asset URLs cannot include credentials","messagePattern":"Remote asset URLs cannot include credentials","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/browser-studio/src/download-remote-asset.ts","lineNumber":42,"sourceCode":"export const downloadRemoteAssetInBrowserStudio = async ({\n\tgetProject,\n\trequest,\n\twriteStaticFile,\n}: {\n\tgetProject: () => VirtualProject;\n\trequest: DownloadRemoteAssetRequest;\n\twriteStaticFile: (request: {\n\t\tcontents: string | ArrayBuffer;\n\t\tfilePath: string;\n\t}) => Promise<void>;\n}): Promise<DownloadRemoteAssetResponse> => {\n\tconst url = new URL(request.url);\n\tif (url.protocol !== 'http:' && url.protocol !== 'https:') {\n\t\tthrow new Error('Only HTTP(S) URLs can be imported');\n\t}\n\n\tif (url.username !== '' || url.password !== '') {\n\t\tthrow new Error('Remote asset URLs cannot include credentials');\n\t}\n\n\tconst abortController = new AbortController();\n\tconst timeout = setTimeout(() => {\n\t\tabortController.abort();\n\t}, remoteAssetDownloadTimeout);\n\n\tlet contents: Uint8Array;\n\ttry {\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\theaders: {accept: remoteAssetAcceptHeader},\n\t\t\t\tsignal: abortController.signal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.name === 'AbortError') {\n\t\t\t\tthrow new Error('Timed out downloading remote asset');","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/remotion-dev/remotion/blob/10db9de07356446fb0edb3c3ae211369b693d18b/packages/browser-studio/src/download-remote-asset.ts#L24-L60","documentation":"Thrown by downloadRemoteAsset in @remotion/browser-studio when the URL contains an userinfo component (`https://user:pass@host/...`) — url.username or url.password is non-empty. Credentials embedded in URLs are rejected because they would be sent to third-party hosts, logged, and persisted into the project, leaking secrets. The check happens before any fetch, and the promise rejects directly.","triggerScenarios":"Calling `downloadRemoteAsset({url: 'https://user:pass@example.com/img.png'})`; URLs copied from browsers/tools that preserve basic-auth userinfo; automated pipelines that embed API keys in asset URLs.","commonSituations":"Users pasting links from password-protected staging servers; scripts embedding CDN tokens as basic auth instead of query params or headers; copied links that silently include `user@host`.","solutions":["Strip the userinfo before importing: rebuild the URL from origin + pathname + search","Host the asset somewhere that does not require basic auth, or put the token in a supported header/query mechanism","Catch the rejection and tell the user the link contains credentials and cannot be imported","Audit pasted URLs in the UI before submission"],"exampleFix":"// before\nawait operations.downloadRemoteAsset({url: rawUrl}); // rawUrl = https://user:pass@cdn.example.com/a.png\n\n// after\nconst u = new URL(rawUrl);\nu.username = '';\nu.password = '';\nawait operations.downloadRemoteAsset({url: u.toString()}); // https://cdn.example.com/a.png","handlingStrategy":"validation","validationCode":"const stripUrlCredentials = (input: string): string => {\n  const u = new URL(input);\n  u.username = '';\n  u.password = '';\n  return u.toString();\n};\nawait operations.downloadRemoteAsset({url: stripUrlCredentials(rawUrl)});","typeGuard":"const hasUrlCredentials = (input: string): boolean => {\n  try { const u = new URL(input); return u.username !== '' || u.password !== ''; } catch { return false; }\n};","tryCatchPattern":"try { await operations.downloadRemoteAsset({url}); } catch (error) { if (error instanceof Error && error.message === 'Remote asset URLs cannot include credentials') { /* ask user for a clean link */ } else throw error; }","preventionTips":["Normalize pasted URLs through `new URL()` and clear username/password before any use","Never build asset URLs by concatenating credentials; use signed URLs or header-based auth where supported"],"tags":["url","security","credentials","asset-import","browser-studio"],"backgroundTag":"url-credentials-rejected","analyzedSha":"10db9de07356446fb0edb3c3ae211369b693d18b","analyzedAt":"2026-08-22T21:45:17.748Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}