TimeoutMiddleware in internal/server/middleware.go answers a request still running after 30 seconds with 408 Request Timeout. 408 tells the client it was too slow sending its request and invites it to send the request again; a limit on the server's own work answers 503 or 504 (the chi middleware.Timeout that GO_HTTP_SERVER_CONVENTIONS.md uses answers 504). handleStatusJSON in internal/server/handlers.go also answers 408 when its 4-second database read runs out, and handleStats writes nothing in that case, so JSONValidationMiddleware turns the empty answer into 500 Internal Server Error.
Failure scenario: a /api/v1/stats request whose route timestamp reads take more than 4 seconds on a busy disk answers 500, the symptom reported in #30; any page that runs over 30 seconds answers 408.
Severity: low
Definition of done
A test, failing before the fix: a handler that outlasts the request limit gets 504 with the JSON error body, and /api/v1/stats with a store that outlasts its 4-second limit gets 504, not 500.
make check passes.
Model: opus-5-5
`TimeoutMiddleware` in `internal/server/middleware.go` answers a request still running after 30 seconds with 408 Request Timeout. 408 tells the client it was too slow sending its request and invites it to send the request again; a limit on the server's own work answers 503 or 504 (the chi `middleware.Timeout` that `GO_HTTP_SERVER_CONVENTIONS.md` uses answers 504). `handleStatusJSON` in `internal/server/handlers.go` also answers 408 when its 4-second database read runs out, and `handleStats` writes nothing in that case, so `JSONValidationMiddleware` turns the empty answer into 500 Internal Server Error.
Failure scenario: a `/api/v1/stats` request whose route timestamp reads take more than 4 seconds on a busy disk answers 500, the symptom reported in https://git.eeqj.de/sneak/routewatch/issues/30; any page that runs over 30 seconds answers 408.
Severity: low
## Definition of done
- A test, failing before the fix: a handler that outlasts the request limit gets 504 with the JSON error body, and `/api/v1/stats` with a store that outlasts its 4-second limit gets 504, not 500.
- `make check` passes.
Model: opus-5-5
clawbot
self-assigned this 2026-10-14 14:50:32 +02:00
Plan: replace the hand-written TimeoutMiddleware with chi's middleware.Timeout, as GO_HTTP_SERVER_CONVENTIONS.md does, so a request past its limit answers 504 with the JSON error body. The handlers already pass the request context to their database reads, so they return when the limit passes. This also removes the suspected race in the current middleware: on a timeout it writes the response from the request goroutine while the handler goroutine may still be writing. Added to the definition of done: no response is written from two goroutines (the new tests run under -race, as make test does), and handleStats/handleStatusJSON answer 504 themselves when their 4-second read runs out, instead of 408 or an empty body.
Model: opus-5-5
Plan: replace the hand-written `TimeoutMiddleware` with chi's `middleware.Timeout`, as `GO_HTTP_SERVER_CONVENTIONS.md` does, so a request past its limit answers 504 with the JSON error body. The handlers already pass the request context to their database reads, so they return when the limit passes. This also removes the suspected race in the current middleware: on a timeout it writes the response from the request goroutine while the handler goroutine may still be writing. Added to the definition of done: no response is written from two goroutines (the new tests run under `-race`, as `make test` does), and `handleStats`/`handleStatusJSON` answer 504 themselves when their 4-second read runs out, instead of 408 or an empty body.
Model: opus-5-5
Built in #80. Two steps beyond the plan: middleware.Timeout now comes after JSONResponseMiddleware, because in the old position that middleware sends a 200 before the 504; and JSONValidationMiddleware answers 504 with the JSON error body when the limit has passed and the handler wrote nothing, because chi's 504 has no body.
Model: opus-5-5
Built in https://git.eeqj.de/sneak/routewatch/pulls/80. Two steps beyond the plan: `middleware.Timeout` now comes after `JSONResponseMiddleware`, because in the old position that middleware sends a 200 before the 504; and `JSONValidationMiddleware` answers 504 with the JSON error body when the limit has passed and the handler wrote nothing, because chi's 504 has no body.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
TimeoutMiddlewareininternal/server/middleware.goanswers a request still running after 30 seconds with 408 Request Timeout. 408 tells the client it was too slow sending its request and invites it to send the request again; a limit on the server's own work answers 503 or 504 (the chimiddleware.TimeoutthatGO_HTTP_SERVER_CONVENTIONS.mduses answers 504).handleStatusJSONininternal/server/handlers.goalso answers 408 when its 4-second database read runs out, andhandleStatswrites nothing in that case, soJSONValidationMiddlewareturns the empty answer into 500 Internal Server Error.Failure scenario: a
/api/v1/statsrequest whose route timestamp reads take more than 4 seconds on a busy disk answers 500, the symptom reported in #30; any page that runs over 30 seconds answers 408.Severity: low
Definition of done
/api/v1/statswith a store that outlasts its 4-second limit gets 504, not 500.make checkpasses.Model: opus-5-5
Plan: replace the hand-written
TimeoutMiddlewarewith chi'smiddleware.Timeout, asGO_HTTP_SERVER_CONVENTIONS.mddoes, so a request past its limit answers 504 with the JSON error body. The handlers already pass the request context to their database reads, so they return when the limit passes. This also removes the suspected race in the current middleware: on a timeout it writes the response from the request goroutine while the handler goroutine may still be writing. Added to the definition of done: no response is written from two goroutines (the new tests run under-race, asmake testdoes), andhandleStats/handleStatusJSONanswer 504 themselves when their 4-second read runs out, instead of 408 or an empty body.Model: opus-5-5
Built in #80. Two steps beyond the plan:
middleware.Timeoutnow comes afterJSONResponseMiddleware, because in the old position that middleware sends a 200 before the 504; andJSONValidationMiddlewareanswers 504 with the JSON error body when the limit has passed and the handler wrote nothing, because chi's 504 has no body.Model: opus-5-5