remotion-dev/remotion · error · Error

Only HTTP(S) URLs can be imported

Error message

Only HTTP(S) URLs can be imported

What it means

Thrown by downloadRemoteAsset in @remotion/browser-studio before any network activity when the URL to import has a protocol other than http: or https:. The importer only fetches remote assets over HTTP(S); data:, blob:, file:, and other schemes are rejected up front as a safety boundary. The promise rejects — this operation does not return an error envelope.

Source

Thrown at packages/browser-studio/src/download-remote-asset.ts:38

		? contents.byteLength
		: contents.sizeInBytes;
};

export const downloadRemoteAssetInBrowserStudio = async ({
	getProject,
	request,
	writeStaticFile,
}: {
	getProject: () => VirtualProject;
	request: DownloadRemoteAssetRequest;
	writeStaticFile: (request: {
		contents: string | ArrayBuffer;
		filePath: string;
	}) => Promise<void>;
}): Promise<DownloadRemoteAssetResponse> => {
	const url = new URL(request.url);
	if (url.protocol !== 'http:' && url.protocol !== 'https:') {
		throw new Error('Only HTTP(S) URLs can be imported');
	}

	if (url.username !== '' || url.password !== '') {
		throw new Error('Remote asset URLs cannot include credentials');
	}

	const abortController = new AbortController();
	const timeout = setTimeout(() => {
		abortController.abort();
	}, remoteAssetDownloadTimeout);

	let contents: Uint8Array;
	try {
		let response: Response;
		try {
			response = await fetch(url, {
				headers: {accept: remoteAssetAcceptHeader},
				signal: abortController.signal,

View on GitHub (pinned to 10db9de073)

Solutions

  1. Validate the protocol before calling downloadRemoteAsset and reject non-http(s) input in the UI
  2. Convert data: URIs to files and write them via writeStaticFile instead of importing by URL
  3. For blob: URLs, read the underlying Blob and write it directly
  4. Show a clear message that only http(s) links can be imported

Example fix

// before
await operations.downloadRemoteAsset({url: pastedText}); // may be data: or blob:

// after
const parsed = new URL(pastedText);
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
  throw new Error('Paste a direct http(s) link to an image');
}
await operations.downloadRemoteAsset({url: parsed.toString()});
Defensive patterns

Strategy: validation

Validate before calling

const isImportableHttpUrl = (input: string): boolean => {
  try {
    const u = new URL(input);
    return u.protocol === 'http:' || u.protocol === 'https:';
  } catch {
    return false;
  }
};
if (!isImportableHttpUrl(url)) throw new Error('Only http(s) links can be imported');
await operations.downloadRemoteAsset({url});

Type guard

const parseImportableUrl = (input: string): URL | null => {
  try {
    const u = new URL(input);
    return u.protocol === 'http:' || u.protocol === 'https:' ? u : null;
  } catch {
    return null;
  }
};

Try / catch

try { await operations.downloadRemoteAsset({url}); } catch (error) { if (error instanceof Error && error.message === 'Only HTTP(S) URLs can be imported') {/* reject input in UI */} else throw error; }

Prevention

When it happens

Trigger: Calling `downloadRemoteAsset({url})` with a `data:image/png;base64,...` URI, a `blob:` URL from the page, `file:` path, or any scheme the URL parser keeps intact. `new URL()` itself throws for truly malformed input, which surfaces as a different error.

Common situations: Paste handler accepting whatever is in the clipboard; drag-and-drop of files that yields blob: URLs; users pasting local file paths; frontend building URLs from unvalidated user text.

Related errors


AI-assisted analysis of remotion-dev/remotion@10db9de073 (2026-08-22). Data as JSON: /api/errors/db566897b546abb8. Report an issue: GitHub.