jackwener/OpenCLI · error · ArgumentError
Unsupported image URL protocol: ${parsed.protocol}
Error message
Unsupported image URL protocol: ${parsed.protocol} What it means
downloadRemoteImage only accepts http: and https: URLs, enforced by the regex /^https?:$/. Any other protocol (ftp:, file:, data:, chrome:, etc.) throws ArgumentError. This blocks unsafe schemes like file:// that would read local resources or bypass intended network fetch semantics.
Source
Thrown at clis/twitter/utils.js:93
);
}
/**
* Download a remote image to a per-call tmp directory. Returns the absolute
* path on success. Caller owns the tmp dir and must clean it up. Throws
* {@link ArgumentError} on bad input or download failure.
*
* @returns {Promise<{ absPath: string, cleanupDir: string }>}
*/
export async function downloadRemoteImage(imageUrl) {
let parsed;
try {
parsed = new URL(imageUrl);
} catch {
throw new ArgumentError(`Invalid image URL: ${imageUrl}`);
}
if (!/^https?:$/.test(parsed.protocol)) {
throw new ArgumentError(`Unsupported image URL protocol: ${parsed.protocol}`);
}
const response = await fetch(imageUrl);
if (!response.ok) {
throw new ArgumentError(`Image download failed: HTTP ${response.status}`);
}
const contentLength = Number(response.headers.get('content-length') || '0');
if (contentLength > MAX_IMAGE_SIZE_BYTES) {
throw new ArgumentError(`Image too large: ${(contentLength / 1024 / 1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024} MB)`);
}
const ext = resolveImageExtension(imageUrl, response.headers.get('content-type'));
const cleanupDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-twitter-'));
const absPath = path.join(cleanupDir, `image${ext}`);
const buffer = Buffer.from(await response.arrayBuffer());
if (buffer.byteLength > MAX_IMAGE_SIZE_BYTES) {
fs.rmSync(cleanupDir, { recursive: true, force: true });
throw new ArgumentError(`Image too large: ${(buffer.byteLength / 1024 / 1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024} MB)`);
}
fs.writeFileSync(absPath, buffer);View on GitHub (pinned to 49907e53dc)
Solutions
- Convert data: URIs to a local file (Buffer.from(base64part, 'base64') written to disk) and pass the local path instead.
- Use an https:// mirror of the image rather than ftp:// or file://.
- If you have a local file, use the local-image code path (resolveImagePath), not downloadRemoteImage.
Example fix
// before
await downloadRemoteImage('file:///home/me/cat.png');
// after
await postWithImage('/home/me/cat.png'); // local path goes through resolveImagePath Defensive patterns
Strategy: type-guard
Validate before calling
const u = new URL(imageUrl);
if (!/^https?:$/.test(u.protocol)) {
throw new Error(`Only http/https supported, got ${u.protocol}`);
} Type guard
function isHttpImageUrl(value) {
try { return /^https?:$/.test(new URL(value).protocol); } catch { return false; }
} Try / catch
try {
await downloadRemoteImage(imageUrl);
} catch (err) {
if (err instanceof ArgumentError && /Unsupported image URL protocol/.test(err.message)) {
const local = await materializeToLocalFile(imageUrl); // data:/file: handlers
return postWithImage(local);
}
throw err;
} Prevention
- Never pass data: or file: URIs to the remote downloader; write them to disk first.
- Accept only http(s) links from upstream callers; validate at the boundary.
- Convert base64 data URIs via Buffer.from(b64, 'base64') into temp files.
When it happens
Trigger: Passing a data: URI, a file:// path, an ftp:// link, or a URL object whose protocol resolved to something non-HTTP — anything where parsed.protocol fails the http/https test.
Common situations: Trying to reuse a data:image/png;base64,... string from an HTML snippet; passing filesystem paths where a URL is expected; internal ftp mirrors; browser-extension style chrome:// image URLs.
Related errors
- Expected a trusted HTTPS bilibili.com video URL without cred
- HLTV parser returned an off-domain URL: ${url.toString()}
- Unsupported Nowcoder URL; expected /discuss/<content-id> or
- Invalid board URL: ${trimmed}
- Invalid image URL: ${imageUrl}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/8fdd2f95c8f70da6.
Report an issue: GitHub.