[BUG] Ctrl+V image paste fails for images >2000px since v2.1.98 — bundled sharp resize error silently swallowed
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Summary
Since v2.1.98, pasting clipboard images with dimensions exceeding 2000px via Ctrl+V silently fails with "No image found in clipboard." Images ≤ 2000px paste normally. This is a regression — v2.1.97 handles all image sizes correctly.
Environment
- Claude Code: 2.1.98+ (tested on 2.1.104, confirmed working on 2.1.97)
- macOS: 15.x (Darwin 25.3.0), Apple Silicon
- Terminal: iTerm2 3.4.15
- Node.js: v20.20.1
Root Cause Analysis
What changed in v2.1.98
The clipboard image processing pipeline was changed. In v2.1.97, the clipboard handler calls:
// v2.1.97 — 3 arguments, no external limits parameter
let A = await eo(Y, Y.length, "png");
In v2.1.98+, it was changed to:
// v2.1.98+ — 4 arguments, passes {maxWidth:2000, maxHeight:2000} limits
let O = await Sa(A, A.length, "png", q);
Why it fails
The sa() function (minified name varies by version) processes images through this flow:
- Calls
sharp(buffer).metadata()to get dimensions — works fine - If dimensions ≤ 2000x2000 → returns image as-is — works fine
- If dimensions > 2000x2000 → calls
sharp(buffer).resize(...).toBuffer()— FAILS - Error caught by
catch { return null }→ caller shows "No image found"
The bundled sharp JS wrapper in cli.js cannot successfully perform the resize().toBuffer() operation. The native module (@img/sharp-darwin-arm64/lib/sharp-darwin-arm64.node v0.34.5) loads correctly, and a standalone sharp installation can resize the same image without issues. The failure is specific to the bundled sharp code within cli.js.
Verification
| Test | Result |
|------|--------|
| osascript -e 'the clipboard as «class PNGf»' | ✅ Works (exit code 0) |
| osascript save to file | ✅ Works (writes valid PNG) |
| Node.js spawn with maxBuffer: 100MB | ✅ Works |
| Standalone sharp(buf).resize(2000, 904).toBuffer() | ✅ Works |
| Bundled sharp resize in cli.js | ❌ Fails silently |
| Paste image ≤ 2000px (no resize needed) | ✅ Works |
| Paste image > 2000px (resize triggered) | ❌ Fails |
Confirmed version bisect
| Version | maxWidth/maxHeight limits | Clipboard calls sa() with limits | Status |
|---------|--------------------------|----------------------------------|--------|
| 2.1.97 (2026-04-08) | Global vars TQ=2000, VQ=2000 | eo(buf, len, "png") — 3 args | ✅ Works |
| 2.1.98 (2026-04-09) | maxWidth:2000, maxHeight:2000 | Sa(buf, len, "png", limits) — 4 args | ❌ Broken |
| 2.1.100 | Same | Ra(buf, len, "png", limits) | ❌ Broken |
| 2.1.104 | Same | sa(buf, len, "png", limits) | ❌ Broken |
What Should Happen?
Images larger than 2000px should either:
- Be resized successfully using sharp before sending, OR
- Fall back to sending the original image if resize fails (the base64 size is often well within the 5MB API limit), OR
- Show a meaningful error message instead of "No image found in clipboard"
Steps to Reproduce
- Copy any image with dimensions > 2000px to clipboard (e.g., a full-screen screenshot on a Retina display)
- Open Claude Code v2.1.98+
- Press Ctrl+V
- Expected: Image is pasted into the conversation
- Actual: Toast notification "No image found in clipboard"
- Workaround: Resize the image to ≤ 2000px before copying, then Ctrl+V works
Proposed Fixes
Fix 1 (Recommended): Pre-resize with sips on macOS before passing to sa():
// After reading the file, before calling sa():
if (process.platform === "darwin" && buf[0] === 0x89 && buf[1] === 0x50) {
let w = buf.readUInt32BE(16), h = buf.readUInt32BE(20);
if (w > maxWidth || h > maxHeight) {
await exec(`sips --resampleWidth ${maxWidth} "${screenshotPath}"`);
buf = fs.readFileSync(screenshotPath);
}
}
Fix 2: Fix the bundled sharp so resize().toBuffer() works correctly in the bundle environment.
Fix 3: In the sa() catch block, fall back to returning the original image when base64 size is within the API limit, even if dimensions exceed the pixel limit:
// Current: throws when dimensions > limit AND sharp fails
if (base64Size <= maxBase64Size && !overDimensionLimit)
return { buffer: original, mediaType };
// Proposed: also fall back when dimensions > limit but base64 is small enough
if (base64Size <= maxBase64Size)
return { buffer: original, mediaType };
Error Messages/Logs
No image found in clipboard. Use ctrl+v to paste images.
(No error is logged — the exception is silently caught with catch { return null })
Is this a regression?
Yes, this worked in v2.1.97
Last Working Version
2.1.97
Claude Code Version
2.1.104
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
Note: This is a different issue from #29776 (ENOBUFS / sandbox-exec). That issue affects ALL images regardless of size. This issue specifically affects only images > 2000px because it triggers the sharp resize code path which fails in the bundled environment.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗