{"record":{"id":"0ec559fd6d792637","repo":"jackwener/OpenCLI","slug":"image-too-large-contentlength-1024-1024-t","errorCode":null,"errorMessage":"Image too large: ${(contentLength / 1024 / 1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024} MB)","messagePattern":"Image too large: (.+?) MB \\(max (.+?) MB\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/utils.js","lineNumber":101,"sourceCode":" * @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 });\n        throw new ArgumentError(`Image too large: ${(buffer.byteLength / 1024 / 1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024} MB)`);\n    }\n    fs.writeFileSync(absPath, buffer);\n    return { absPath, cleanupDir };\n}\n\n/**\n * Attach a single image to the current /compose/post composer. Tries the\n * native CDP file-input bridge first; falls back to a base64 DataTransfer\n * shim if the bridge is missing or rejects with \"Unknown action\" /\n * \"not supported\". Throws on hard failures.","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/utils.js#L83-L119","documentation":"downloadRemoteImage reads the Content-Length header and throws ArgumentError if it exceeds MAX_IMAGE_SIZE_BYTES (20MB). This pre-flight check avoids buffering an oversized body before the same cap is re-checked against the actual downloaded byte length. Note: a missing Content-Length header is treated as 0, so this only fires when the server reports the size.","triggerScenarios":"Fetching a remote image whose Content-Length header exceeds 20*1024*1024 bytes: huge PNGs, long GIFs, or servers misreporting content-length larger than the actual body.","commonSituations":"Direct links to full-resolution photos (DSLR JPEGs >20MB); long animated GIFs hosted on CDNs; archives/media renamed to image extensions but served with correct large sizes.","solutions":["Choose a smaller variant of the image (CDN resize params like ?w=1600 or a thumbnail URL).","Download, compress locally (`magick in.jpg -quality 85 out.jpg`), then use the local-path API.","For GIFs, use a shorter/resized version (gifsicle) or convert to video.","If the server lies about content-length while the real body is small, that cap lives in clis/twitter/utils.js (MAX_IMAGE_SIZE_BYTES) — but X's own upload limits still apply."],"exampleFix":"// before\nawait downloadRemoteImage('https://cdn.example.com/originals/cat.png'); // 48 MB\n// after\nawait downloadRemoteImage('https://cdn.example.com/originals/cat.png?w=1600&q=80'); // ~2 MB","handlingStrategy":"validation","validationCode":"const head = await fetch(imageUrl, { method: 'HEAD' });\nconst len = Number(head.headers.get('content-length') || '0');\nif (len > 20 * 1024 * 1024) {\n  throw new Error(`Remote image too large: ${(len / 1048576).toFixed(1)} MB; use a smaller variant`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await downloadRemoteImage(imageUrl);\n} catch (err) {\n  if (err instanceof ArgumentError && /Image too large/.test(err.message)) {\n    const local = await downloadAndShrink(imageUrl, 1600);\n    return postWithImage(local);\n  }\n  throw err;\n}","preventionTips":["Prefer CDN resize parameters (?w=1600&q=80) or thumbnail variants of large originals.","HEAD-check content-length before downloading big links.","Remember X's own limits (~5MB images, 15MB GIFs); keep media well under both caps."],"tags":["file-size","http","upload-limits"],"backgroundTag":"payload-too-large","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}