da44f1947b
LocalFsStorage.get() returns an fs.createReadStream, which is lazy: it resolves immediately and opens the file on a later tick, so an ENOENT arrives after the await returned and outside the route's try/catch. An unhandled 'error' event is a process-level throw Express cannot catch — the backend exits and every gallery goes blank until the container restarts. gallery.js had ten .pipe(res) calls and zero error handlers. pipeStreamToResponse attaches the missing handler: a vanished source becomes a 404 (410 for a prepared zip), anything else a 500, and a source that dies mid-response destroys the connection rather than rewriting a status already on the wire. Headers staged for the file are cleared first — Express does not overwrite an existing Content-Type, and a surviving Cache-Control would let a transient 404 be cached as a broken tile for up to an hour. It also releases the source when a client hangs up. Applied to all eight streaming responses, not just the thumbnail route. Stable twin of #1133, reduced: the tier-race half does not apply here because ensureThumbnailAtWidth does not exist on this branch.