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 used

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Resize or recompress the image (e.g. `sips -Z 2000`, ImageMagick `convert -resize`, `sharp`) so it fits under MAX_IMAGE_SIZE_BYTES, then retry
  2. Re-encode to a more compact format (large PNG -> JPEG/WebP) to cut byte size
  3. Use a smaller variant of the image if the host serves one (thumbnail/preview URL, CDN resize params like `?w=1200`)
  4. 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

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


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