{"record":{"id":"8734665a318893ce","repo":"jackwener/OpenCLI","slug":"invalid-image-url-imageurl","errorCode":null,"errorMessage":"Invalid image URL: ${imageUrl}","messagePattern":"Invalid image URL: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/utils.js","lineNumber":90,"sourceCode":"    }\n    throw new ArgumentError(\n        `Unsupported remote image format \"${normalizedContentType || 'unknown'}\". Supported: jpg, jpeg, png, gif, webp`,\n    );\n}\n\n/**\n * Download a remote image to a per-call tmp directory. Returns the absolute\n * path on success. Caller owns the tmp dir and must clean it up. Throws\n * {@link ArgumentError} on bad input or download failure.\n *\n * @returns {Promise<{ absPath: string, cleanupDir: string }>}\n */\nexport async function downloadRemoteImage(imageUrl) {\n    let parsed;\n    try {\n        parsed = new URL(imageUrl);\n    } catch {\n        throw new ArgumentError(`Invalid image URL: ${imageUrl}`);\n    }\n    if (!/^https?:$/.test(parsed.protocol)) {\n        throw new ArgumentError(`Unsupported image URL protocol: ${parsed.protocol}`);\n    }\n    const response = await fetch(imageUrl);\n    if (!response.ok) {\n        throw new ArgumentError(`Image download failed: HTTP ${response.status}`);\n    }\n    const contentLength = Number(response.headers.get('content-length') || '0');\n    if (contentLength > MAX_IMAGE_SIZE_BYTES) {\n        throw new ArgumentError(`Image too large: ${(contentLength / 1024 / 1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024} MB)`);\n    }\n    const ext = resolveImageExtension(imageUrl, response.headers.get('content-type'));\n    const cleanupDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-twitter-'));\n    const absPath = path.join(cleanupDir, `image${ext}`);\n    const buffer = Buffer.from(await response.arrayBuffer());\n    if (buffer.byteLength > MAX_IMAGE_SIZE_BYTES) {\n        fs.rmSync(cleanupDir, { recursive: true, force: true });","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/utils.js#L72-L108","documentation":"downloadRemoteImage first parses the given URL with the URL constructor; if parsing fails it throws ArgumentError `Invalid image URL: <input>`. This catches syntactically malformed URLs before any network request is made.","triggerScenarios":"Passing a string that `new URL()` cannot parse: missing scheme (\"example.com/img.png\"), spaces or unencoded special characters, empty string, a file path mistakenly passed to the remote-image path, or undefined/null coerced to a string.","commonSituations":"Copy-pasting a URL that lost its https:// prefix; shell mangling of '&', '?', or spaces without quotes; forgetting to download a file and passing its local path to the URL-based API; template-string variables that interpolated to empty.","solutions":["Ensure the URL includes the scheme: prepend https:// if missing.","Encode the URL: wrap in encodeURI() or quote it in the shell to protect ?& and spaces.","Confirm you are passing a URL, not a local file path — local paths belong to resolveImagePath.","Sanity-check in Node: `new URL(value)` in a REPL should not throw before calling the API."],"exampleFix":"// before\nawait downloadRemoteImage('cdn.example.com/cat.png'); // no scheme\n// after\nconst url = raw.startsWith('http') ? raw : `https://${raw}`;\nawait downloadRemoteImage(encodeURI(url));","handlingStrategy":"validation","validationCode":"let parsed;\ntry { parsed = new URL(imageUrl); } catch {\n  throw new Error(`Malformed image URL: ${JSON.stringify(imageUrl)}`);\n}\nif (!parsed.protocol.startsWith('http')) {\n  throw new Error(`Scheme must be http(s): ${imageUrl}`);\n}","typeGuard":"function isHttpUrl(value) {\n  if (typeof value !== 'string' || value.length === 0) return false;\n  try { return new URL(value).protocol.startsWith('http'); } catch { return false; }\n}","tryCatchPattern":"try {\n  await downloadRemoteImage(imageUrl);\n} catch (err) {\n  if (err instanceof ArgumentError && err.message.startsWith('Invalid image URL')) {\n    return downloadRemoteImage(new URL(imageUrl, 'https://example.com').href); // resolve relative\n  }\n  throw err;\n}","preventionTips":["Prepend https:// when the scheme is missing.","Quote URLs in the shell and encode query strings/spaces (encodeURI).","Distinguish local paths vs URLs in your CLI arg handling before dispatching."],"tags":["url-parsing","input-validation","arguments"],"backgroundTag":"invalid-url","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}