medlab / case

medlab

what got hurt, and what now fails loudly

↑ sublayer

2026-10-14 · fixed: partial

An image decoder that works on desktop also handles phone photos

BehavioralIntegration Tests

now fails loudly Browser tests disable the bitmap decoder and require a resized JPEG upload, while undecodable images must show an actionable error without sending a request.

What broke

Wortling is a web app where a learner photographs German text they encounter and submits the photo, and the app turns the selected encounters into Anki flashcards for that learner. The capture page is the page in the trial where a photo is submitted. It read the selected photo with createImageBitmap, the browser's built-in image decoder, and used only that path. That decoder does not read HEIC, the format an iPhone's photo library produces. The page's error handler also added network-retry instructions to every exception, including a local decoding failure, so a learner whose photo could not be decoded was told to retry sending rather than told the photo could not be read.

How it showed

A learner using Firefox on an iPhone opened the capture page and selected a photo from the standard iOS photo picker. The page displayed Type error followed by instructions to retry sending. No new capture reached the server. The precise decoder failure on that physical phone was not available to inspect, so the symptom was the only evidence of where the upload stopped.

What was watching

API integration tests covered image validation and storage after receiving a JPEG or PNG payload, which is after the browser had already decoded the photo. Desktop browser checks covered the learner's status page rendering and text capture, never a phone-format photo. Neither exercised local photo preparation when the bitmap decoder rejected the input. The customer reported the failure during the phone trial.

Where it sits

This behavioral failure occurs before the API boundary, inside the browser, so the server-side image validation never saw the image. Integration tests were present but started after the browser's conversion step or omitted image input. A browser test now submits a real image through that conversion and request path.

What we did

The page now loads photos through an image element and converts them to a bounded JPEG with a canvas. Temporary object URLs are released after success or failure. Preparation, sending, and report-refresh failures have distinct messages. A local WebKit check converted a generated HEIC image to a 2000-by-1000 JPEG. Physical-device confirmation remains outstanding; the exact cause of the original phone exception is not asserted.

What now fails loudly

The browser test forces createImageBitmap to throw TypeError and requires one valid JPEG upload with the expected resized dimensions. Restoring the original page makes that test time out waiting for a successful capture. A second test supplies undecodable image bytes and requires an actionable preparation error with no upload request. CI installs the browser and runs these tests with the backend suite.