Repository navigation
fix(errors): serve a 500 page when the disk can't supply one - #3396
Merged
Merged
Conversation
Django's `django.views.defaults.server_error` loads 500.html off disk and catches only `TemplateDoesNotExist`. On a device whose storage or file-descriptor budget has already failed — which is the condition that produced the original 500 — that load raises an `OSError` of its own, escapes `handle_uncaught_exception`, and uvicorn drops the connection. The operator gets a browser error instead of a page telling them their card needs attention. Seen in the field on a Pi 5 that ran out of file descriptors: three `OperationalError: unable to open database file` events (the real fault, Sentry ANTHIAS-63) buried under twenty-four `OSError: [Errno 24] Too many open files: '.../app/templates/500.html'` raised while rendering the error page for them (Sentry ANTHIAS-62). The cascade is worse than noise — the tracker ranks by event count, so the symptom outranked its own cause 8:1. Register a `handler500` that tries the branded template and, when anything at all goes wrong reading or rendering it, serves a page that has been in memory since import: no extends chain, no stylesheet link, no static reference. 500.html extends _error.html, which pulls a layout and a stylesheet, so the working path is several reads deep and every one of them is covered. Logged at warning, not error: the exception that got us here has already been reported, and re-reporting its fallout once per request is what buried the cause. It still lands as a breadcrumb. Only the 500 handler is replaced. The 4xx handlers read templates the same way, but they are reached by ordinary requests on healthy devices; this one is reached because something is already broken, which is what makes a disk read the wrong thing to depend on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
There was a problem hiding this comment.
Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.
vpetersson
approved these changes
Oct 18, 2026
vpetersson-bot
force-pushed
the
agent/anthias-claude/d2d2a207b1af
branch
from
October 19, 2026 08:53
8c931e2 to
94504f7
Compare
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What's wrong today
Django's
django.views.defaults.server_errorloads500.htmloff diskand catches only
TemplateDoesNotExist. On a device whose storage orfile-descriptor budget has already failed — which is the condition that
produced the original 500 — that load raises an
OSErrorof its own.Nothing catches it: it escapes
handle_uncaught_exception, uvicorn dropsthe connection, and the operator gets a browser error rather than a page
telling them their card needs attention.
Seen in the field on a Pi 5 that ran out of file descriptors: three
OperationalError: unable to open database fileevents — the real fault,error-tracker issue ANTHIAS-63 — buried under twenty-four
OSError: [Errno 24] Too many open files: '.../app/templates/500.html'raised while rendering the error page for them (ANTHIAS-62). The cascade
is worse than noise: the tracker ranks by event count, so the symptom
outranked its own cause 8:1.
What this does
Registers a
handler500that tries the branded template and, whenanything at all goes wrong reading or rendering it, serves a page that
has been in memory since import — no extends chain, no stylesheet link,
no static reference.
500.htmlextends_error.html, which pulls alayout and a stylesheet, so the working path is several reads deep and
every one of them is covered.
Logged at warning rather than error: the exception that got us here has
already been reported, and re-reporting its fallout once per request is
what buried the cause. It still lands as a breadcrumb on the next event.
Only the 500 handler is replaced. The 4xx handlers read templates the
same way and would fail the same way, but they are reached by ordinary
requests on healthy devices; this one is reached because something is
already broken, which is what makes a disk read the wrong thing to
depend on here.
How I know it's right
A throwaway parametrized run against both handlers, with
loader.get_templateraisingEMFILEand a view raisingOperationalError:Committed coverage in
tests/test_error_pages.py:EMFILE,EIO,ENOTCLEAN(117 — the errnobehind the corrupted-ext4 crash-loop that
ResilientWhiteNoiseMiddlewareexists for) andTemplateSyntaxError,and the body it serves contains no
<linkand no/static/;handler500to this view — ahandler nobody routes to fixes nothing, and a typo there would only
surface during a real outage;
read fails, client still gets a 500 with a body.
uv run pytest -m "not integration"— 2376 passed, 3 skipped.ruff check,ruff format --checkandmypy .are clean (the twotools/image_builderimport-not-found errors are pre-existing, from theoptional
docker-image-builderdependency group).🤖 Generated with Claude Code