jackwener/OpenCLI · error · ArgumentError
Image too large: ${(buffer.byteLength / 1024 / 1024).toFixed
Error message
Image too large: ${(buffer.byteLength / 1024 / 1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024} MB) What it means
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.
Source
Thrown at clis/twitter/utils.js:109
}
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);
return { absPath, cleanupDir };
}
/**
* Attach a single image to the current /compose/post composer. Tries the
* native CDP file-input bridge first; falls back to a base64 DataTransfer
* shim if the bridge is missing or rejects with "Unknown action" /
* "not supported". Throws on hard failures.
*
* After upload it polls the DOM briefly to confirm the preview thumbnail
* actually rendered — without this, a 200 from setFileInput could mask a
* silent-no-attachment post.
*
* @param {object} page - OpenCLI page handle.
* @param {string} absImagePath - Already-validated absolute path.
* @param {string} [fileInputSelector] - Override (post.js historically usedView on GitHub (pinned to 49907e53dc)
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
Example fix
// before
await downloadRemoteImage('https://cdn.example.com/huge-photo.png'); // 8.3 MB -> ArgumentError
// after
import sharp from 'sharp';
const small = await sharp(await (await fetch(url)).arrayBuffer()).resize({ width: 1600 }).jpeg({ quality: 80 }).toBuffer();
fs.writeFileSync('/tmp/img.jpg', small);
// then attach /tmp/img.jpg instead of the remote URL Defensive patterns
Strategy: validation
Validate before calling
const res = await fetch(imageUrl);
const len = Number(res.headers.get('content-length') || '0');
if (len > MAX_IMAGE_SIZE_BYTES) throw new Error(`Refusing to attach ${url}: ${(len/1048576).toFixed(1)} MB exceeds limit`); Type guard
function isWithinImageLimit(buffer) {
return Buffer.isBuffer(buffer) && buffer.byteLength <= MAX_IMAGE_SIZE_BYTES;
} Try / catch
try {
const { absPath, cleanupDir } = await downloadRemoteImage(url);
// use absPath
} catch (err) {
if (/^Image too large:/.test(err.message)) {
console.error('Compress or resize the image before attaching:', err.message);
} else { throw err; }
} finally {
if (typeof cleanupDir === 'string') fs.rmSync(cleanupDir, { recursive: true, force: true });
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to read image dimensions for ${filePath}
- FILE_TOO_LARGE
- INVALID_ARGUMENT
- INVALID_ARGUMENT
- ${label} is required
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/ed86afde292acff3.
Report an issue: GitHub.