{"record":{"id":"ed86afde292acff3","repo":"jackwener/OpenCLI","slug":"image-too-large-buffer-bytelength-1024-102","errorCode":null,"errorMessage":"Image too large: ${(buffer.byteLength / 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":109,"sourceCode":"    }\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.\n *\n * After upload it polls the DOM briefly to confirm the preview thumbnail\n * actually rendered — without this, a 200 from setFileInput could mask a\n * silent-no-attachment post.\n *\n * @param {object} page - OpenCLI page handle.\n * @param {string} absImagePath - Already-validated absolute path.\n * @param {string} [fileInputSelector] - Override (post.js historically used","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/utils.js#L91-L127","documentation":"Thrown by downloadRemoteImage in clis/twitter/utils.js when the downloaded image buffer exceeds MAX_IMAGE_SIZE_BYTES. This is a post-download safety check: even when the server omits or lies about the content-length header, the full response body is buffered and measured before writing to disk, so oversized images never reach the composer upload path. The temp dir is cleaned up before throwing.","triggerScenarios":"Calling downloadRemoteImage(url) (or the `downloaded` flow that wraps it) with a remote image whose byte size, after fetching the full response body, exceeds MAX_IMAGE_SIZE_BYTES; typically via `opencli twitter post --image <url>` pointing at a multi-MB photo.","commonSituations":"Attaching unscaled photos straight from a camera or a stock-photo CDN (5-10MB JPEGs); image hosts that strip the content-length header so the early header check at line 100 is skipped and the body check at line 107 fires; animated GIFs or PNG screenshots larger than the limit.","solutions":["Resize or recompress the image (e.g. `sips -Z 2000`, ImageMagick `convert -resize`, `sharp`) so it fits under MAX_IMAGE_SIZE_BYTES, then retry","Re-encode to a more compact format (large PNG -> JPEG/WebP) to cut byte size","Use a smaller variant of the image if the host serves one (thumbnail/preview URL, CDN resize params like `?w=1200`)","Download locally, check the size yourself, and attach a local path through the upload flow instead of a remote URL"],"exampleFix":"// before\nawait downloadRemoteImage('https://cdn.example.com/huge-photo.png'); // 8.3 MB -> ArgumentError\n// after\nimport sharp from 'sharp';\nconst small = await sharp(await (await fetch(url)).arrayBuffer()).resize({ width: 1600 }).jpeg({ quality: 80 }).toBuffer();\nfs.writeFileSync('/tmp/img.jpg', small);\n// then attach /tmp/img.jpg instead of the remote URL","handlingStrategy":"validation","validationCode":"const res = await fetch(imageUrl);\nconst len = Number(res.headers.get('content-length') || '0');\nif (len > MAX_IMAGE_SIZE_BYTES) throw new Error(`Refusing to attach ${url}: ${(len/1048576).toFixed(1)} MB exceeds limit`);","typeGuard":"function isWithinImageLimit(buffer) {\n  return Buffer.isBuffer(buffer) && buffer.byteLength <= MAX_IMAGE_SIZE_BYTES;\n}","tryCatchPattern":"try {\n  const { absPath, cleanupDir } = await downloadRemoteImage(url);\n  // use absPath\n} catch (err) {\n  if (/^Image too large:/.test(err.message)) {\n    console.error('Compress or resize the image before attaching:', err.message);\n  } else { throw err; }\n} finally {\n  if (typeof cleanupDir === 'string') fs.rmSync(cleanupDir, { recursive: true, force: true });\n}","preventionTips":["Pre-check the image dimensions/size and downscale before attaching","Prefer JPEG/WebP over PNG/GIF for photos to keep byte size small","Use CDN resize parameters on remote URLs (?w= / thumbnail variants)","Surface a friendly 'compress your image' hint in any tooling that wraps this CLI"],"tags":["image","file-size","validation","twitter-cli"],"backgroundTag":"file-too-large","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}