Skip to content

Recompress oversized images under Bluesky's blob limit - #1343

Merged
gesteves merged 1 commit into
releasefrom
claude/cloudflare-bluesky-photo-sharing-bbyekw
Jul 22, 2026
Merged

Recompress oversized images under Bluesky's blob limit#1343
gesteves merged 1 commit into
releasefrom
claude/cloudflare-bluesky-photo-sharing-bbyekw

Conversation

@gesteves

Copy link
Copy Markdown
Owner

Problem

Sharing photos to Bluesky was failing in production with:

RuntimeError: Failed to create app.bsky.feed.post record:
{"error":"InvalidRequest","message":"Invalid app.bsky.feed.post record: blob too big
(maximum 2000000, got 2998293) at $.record.embed.images[0].image"}

Bluesky rejects any post embed whose image blob exceeds 2,000,000 bytes. The failure is reported when the record is created (uploadBlob itself accepts the blob, so it surfaces later at createRecord).

The Cloudflare migration replaced Thumbor's max_bytes filter — which walked JPEG quality down until the image fit under the limit — with a fixed quality: 80 in Photo#bluesky_url, since Cloudflare has no equivalent filter. For high-detail photos, quality 80 at up to 4000px still produces blobs over 2 MB. And because bluesky_url is deterministic, every Sidekiq retry fetched the exact same oversized blob and failed identically — which is why "every time it retries it's getting the same photo."

Fix

Enforce the blob limit in the uploader itself (Bluesky#upload_photo):

  • When a fetched image exceeds MAX_BLOB_SIZE (2,000,000 bytes), recompress it as JPEG before uploading.
  • Recompression walks a series of steps — dropping JPEG quality first (70 → 60 → 50), then scaling the image down (85% / 70% / 55% at quality 40) — and uploads the first result at or under a 1,950,000-byte target (50 KB of headroom under the hard limit).
  • Images already under the limit are uploaded untouched, so the common case is unchanged.

This guarantees the blob converges to a size Bluesky accepts, so oversized photos now share successfully instead of retrying forever.

Testing

Added spec/lib/bluesky_spec.rb coverage for:

  • An image over the blob limit being recompressed (via MiniMagick) before upload, and the original oversized blob never being uploaded.
  • An image under the limit being uploaded as-is, without invoking recompression.

Note: the test suite runs inside the project's Docker container, which wasn't available in this remote environment, so these specs were not executed here. Both changed files pass ruby -c syntax checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_016fUUqvz6wwWoeHJ4DcWncy


Generated by Claude Code

Bluesky rejects post embeds whose image blobs exceed 2,000,000 bytes,
surfacing as "blob too big" at $.record.embed.images[].image when the
record is created. The Cloudflare migration replaced Thumbor's max_bytes
filter with a fixed JPEG quality, which no longer guarantees the blob
stays under the limit. Because bluesky_url is deterministic, an oversized
photo produced the same too-big blob on every Sidekiq retry and could
never be shared.

Enforce the limit in the uploader: when a fetched image exceeds the blob
limit, recompress it as JPEG — dropping quality first, then scaling down —
and upload the first result that fits. Images already under the limit are
uploaded untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fUUqvz6wwWoeHJ4DcWncy
@gesteves
gesteves merged commit 7a4a1b3 into release Jul 22, 2026
4 checks passed
@gesteves
gesteves deleted the claude/cloudflare-bluesky-photo-sharing-bbyekw branch July 22, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants