fix(images): pick the decoder from the bytes, not the extension #183

Merged
cameron merged 1 commits from fix/sniff-image-format into master 2026-10-17 04:24:26 +00:00
Owner

Problem

Medium preview generation failed for …/Screenshot_2014-11-17-21-30-31.jpg: Format error decoding Jpeg: Illegal start bytes:8950

The file is a PNG saved with a .jpg name (0x89 0x50 is the start of the PNG header). image::open chooses the decoder from the file extension, so these files fail every time.

Fix

New file_types::open_image reads the file header to pick the format (ImageReader::with_guessed_format) and falls back to the extension when it doesn't recognize it. It replaces image::open at every place that decodes a file:

  • thumbnails and previews (thumbnails.rs)
  • perceptual hashes
  • insight vision input
  • manual face crops

The circular-thumbnail path already worked this way.

Note: earlier scans probably left a .unsupported marker next to these files' thumbnails, which makes later scans skip them. Delete those markers to get the 200px thumbnails regenerated. Medium previews are made on request, so they work as soon as this is deployed.

Tests

  • open_image_sniffs_format_over_extension: a PNG saved as .jpg fails with image::open and decodes with open_image.
  • Full suite and clippy are clean.

🤖 Generated with Claude Code

## Problem `Medium preview generation failed for …/Screenshot_2014-11-17-21-30-31.jpg: Format error decoding Jpeg: Illegal start bytes:8950` The file is a PNG saved with a `.jpg` name (`0x89 0x50` is the start of the PNG header). `image::open` chooses the decoder from the file extension, so these files fail every time. ## Fix New `file_types::open_image` reads the file header to pick the format (`ImageReader::with_guessed_format`) and falls back to the extension when it doesn't recognize it. It replaces `image::open` at every place that decodes a file: - thumbnails and previews (`thumbnails.rs`) - perceptual hashes - insight vision input - manual face crops The circular-thumbnail path already worked this way. **Note:** earlier scans probably left a `.unsupported` marker next to these files' thumbnails, which makes later scans skip them. Delete those markers to get the 200px thumbnails regenerated. Medium previews are made on request, so they work as soon as this is deployed. ## Tests - `open_image_sniffs_format_over_extension`: a PNG saved as `.jpg` fails with `image::open` and decodes with `open_image`. - Full suite and clippy are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
cameron added 1 commit 2026-10-17 04:24:05 +00:00
`image::open` chooses the decoder by extension, so a PNG saved as .jpg
(Android screenshots, chat exports) failed thumbnails, previews,
perceptual hashes, insights and face crops with "Illegal start bytes:
8950". New `file_types::open_image` sniffs the header and falls back to
the extension when it's unknown; every decode site uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cameron force-pushed fix/sniff-image-format from 9222d3e3ae to b95adb4f77 2026-10-17 04:24:05 +00:00 Compare
cameron merged commit 22d28ee80f into master 2026-10-17 04:24:26 +00:00
cameron deleted branch fix/sniff-image-format 2026-10-17 04:24:26 +00:00
Sign in to join this conversation.