jackwener/OpenCLI · error · ArgumentError

Image too large: ${(contentLength / 1024 / 1024).toFixed(1)}

Error message

Image too large: ${(contentLength / 1024 / 1024).toFixed(1)} MB (max ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024} MB)

What it means

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.

Source

Thrown at clis/twitter/utils.js:101

 * @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);
    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.

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Choose a smaller variant of the image (CDN resize params like ?w=1600 or a thumbnail URL).
  2. Download, compress locally (`magick in.jpg -quality 85 out.jpg`), then use the local-path API.
  3. For GIFs, use a shorter/resized version (gifsicle) or convert to video.
  4. 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.

Example fix

// before
await downloadRemoteImage('https://cdn.example.com/originals/cat.png'); // 48 MB
// after
await downloadRemoteImage('https://cdn.example.com/originals/cat.png?w=1600&q=80'); // ~2 MB
Defensive patterns

Strategy: validation

Validate before calling

const head = await fetch(imageUrl, { method: 'HEAD' });
const len = Number(head.headers.get('content-length') || '0');
if (len > 20 * 1024 * 1024) {
  throw new Error(`Remote image too large: ${(len / 1048576).toFixed(1)} MB; use a smaller variant`);
}

Try / catch

try {
  await downloadRemoteImage(imageUrl);
} catch (err) {
  if (err instanceof ArgumentError && /Image too large/.test(err.message)) {
    const local = await downloadAndShrink(imageUrl, 1600);
    return postWithImage(local);
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/0ec559fd6d792637. Report an issue: GitHub.