fix(screenshots): catch empty-bytes tiled result and set ERROR on falsy image

Two bugs that could cause blank PDFs to re-trigger indefinitely:

1. webdriver.py: tiled fallback used `if img is None:` which let b""
   (returned by combine_screenshot_tiles on an empty tile list) pass
   through silently. Changed to `if not img:` to catch both cases.

2. screenshots.py: compute_and_cache had no else branch after
   `if image: cache_payload.update(image)`. When get_screenshot returned
   None or b"" without raising, status stayed at COMPUTING instead of
   ERROR, causing is_computing_stale() to re-trigger the task
   indefinitely after THUMBNAIL_ERROR_CACHE_TTL. Added
   `else: cache_payload.error()` so failed screenshots always transition
   to ERROR and use the controlled TTL back-off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Elizabeth Thompson
2026-06-16 00:31:23 +00:00
parent 495a9a3113
commit f8cf65404a
4 changed files with 66 additions and 5 deletions

View File

@@ -155,6 +155,26 @@ class TestCacheOnlyOnSuccess:
assert cached_value["status"] == "Updated"
assert cached_value["image"] is not None
def test_cache_error_status_when_screenshot_returns_empty_bytes(
self, mocker: MockerFixture, screenshot_obj: BaseScreenshot, mock_user: MagicMock
) -> None:
"""Empty bytes from get_screenshot must set ERROR, not leave COMPUTING."""
mocker.patch(DISTRIBUTED_LOCK_PATH)
mocker.patch(BASE_SCREENSHOT_PATH + ".get_from_cache_key", return_value=None)
mocker.patch(
BASE_SCREENSHOT_PATH + ".get_screenshot",
return_value=b"",
)
BaseScreenshot.cache = MockCache()
screenshot_obj.compute_and_cache(user=mock_user, force=True)
cache_key = screenshot_obj.get_cache_key()
cached_value = BaseScreenshot.cache.get(cache_key)
assert cached_value is not None
assert cached_value["status"] == "Error"
assert cached_value.get("image") is None
def test_computing_status_written_to_cache_early(
self,
mocker: MockerFixture,