fix(importer): catch PIL OSError during transparency + phash blocks, skip as invalid_image instead of letting Celery autoretry loop forever
Operator hit a corrupt JPEG in the IR set 2026-05-25: PIL.verify() only validates header structure but doesn't catch truncated/broken pixel data. The error surfaces later in _transparency_pct (via getchannel 'A' -> load) or compute_phash (load) — both blow up with OSError 'broken data stream when reading image file'. Celery's autoretry_for then bounces the same file forever instead of marking it skipped. Wrap both PIL.load-triggering call sites with try/except OSError -> ImportResult(status=skipped, skip_reason=invalid_image). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -280,7 +280,18 @@ class Importer:
|
||||
)
|
||||
|
||||
if self.settings.skip_transparent and has_alpha:
|
||||
pct = self._transparency_pct(source)
|
||||
try:
|
||||
pct = self._transparency_pct(source)
|
||||
except OSError as exc:
|
||||
# PIL.verify() at line 263 only validates header structure;
|
||||
# truncated/corrupt pixel data only surfaces when load()
|
||||
# actually decodes (here via getchannel('A')). Convert to
|
||||
# invalid_image skip so the Celery autoretry loop doesn't
|
||||
# bounce the same broken file forever.
|
||||
return ImportResult(
|
||||
status="skipped", skip_reason=SkipReason.invalid_image,
|
||||
error=f"PIL load failed during transparency check: {exc}",
|
||||
)
|
||||
if pct >= self.settings.transparency_threshold:
|
||||
return ImportResult(
|
||||
status="skipped", skip_reason=SkipReason.too_transparent,
|
||||
@@ -302,8 +313,16 @@ class Importer:
|
||||
# Perceptual near-dup (images only; videos keep phash NULL).
|
||||
phash = None
|
||||
if not is_video(source):
|
||||
with Image.open(source) as im:
|
||||
phash = compute_phash(im)
|
||||
try:
|
||||
with Image.open(source) as im:
|
||||
phash = compute_phash(im)
|
||||
except OSError as exc:
|
||||
# Same rationale as the transparency-check guard above:
|
||||
# broken-pixel-data files pass verify() but blow up here.
|
||||
return ImportResult(
|
||||
status="skipped", skip_reason=SkipReason.invalid_image,
|
||||
error=f"PIL load failed during phash compute: {exc}",
|
||||
)
|
||||
if phash is not None:
|
||||
cand_rows = self.session.execute(
|
||||
select(
|
||||
|
||||
Reference in New Issue
Block a user