HEAD requests answer 405 on every route, and a 405 on the JSON routes says "Internal server error" #111

Closed
opened 2026-10-19 22:35:19 +02:00 by clawbot · 1 comment
Collaborator

Where: setupRoutes in internal/server/routes.go; JSONValidationMiddleware in internal/server/middleware.go.

Every route is registered with r.Get and chi's middleware.GetHead is not used, so chi does not route HEAD to the GET handler. Three wrong answers follow:

  • HEAD on any route, the health check included, answers 405 with an empty body. Uptime monitors and link checkers that probe with HEAD mark the service down. (The image's own HEALTHCHECK uses curl -sf, a GET, so deploy health is unaffected.)
  • On the /api/v1 and /ip sub-routers JSONValidationMiddleware wraps chi's 405: the captured answer has no Content-Type and no body, so the middleware treats it as JSON, finds the body empty, keeps the 405 and writes {"status":"error","error":{"msg":"Internal server error","code":405}} (plus @meta). A POST /api/v1/stats is told the server failed.
  • GET /api/v1/nothing answers chi's default text/plain "404 page not found", while GET /api/v1/as/4200000000 answers a JSON 404 envelope; README describes the JSON envelope for the API's answers.

Severity: low.

Definition of done

  • HEAD is served for every GET route (chi middleware.GetHead or explicit handlers).
  • The JSON sub-routers have NotFound and MethodNotAllowed handlers that write the JSON envelope with "Not found" and "Method not allowed" and the right code; JSONValidationMiddleware no longer invents "Internal server error" for a 4xx with an empty body.
  • Tests, failing before the fix, through s.router: HEAD /api/v1/stats answers 200 (today 405); POST /api/v1/stats answers 405 with error.msg "Method not allowed" (today "Internal server error"); GET /api/v1/nothing answers a JSON 404 envelope (today text/plain).
  • make check passes.

Model: fable-5-1

Where: `setupRoutes` in `internal/server/routes.go`; `JSONValidationMiddleware` in `internal/server/middleware.go`. Every route is registered with `r.Get` and chi's `middleware.GetHead` is not used, so chi does not route HEAD to the GET handler. Three wrong answers follow: - `HEAD` on any route, the health check included, answers 405 with an empty body. Uptime monitors and link checkers that probe with HEAD mark the service down. (The image's own `HEALTHCHECK` uses `curl -sf`, a GET, so deploy health is unaffected.) - On the `/api/v1` and `/ip` sub-routers `JSONValidationMiddleware` wraps chi's 405: the captured answer has no Content-Type and no body, so the middleware treats it as JSON, finds the body empty, keeps the 405 and writes `{"status":"error","error":{"msg":"Internal server error","code":405}}` (plus `@meta`). A `POST /api/v1/stats` is told the server failed. - `GET /api/v1/nothing` answers chi's default `text/plain` "404 page not found", while `GET /api/v1/as/4200000000` answers a JSON 404 envelope; README describes the JSON envelope for the API's answers. Severity: low. ## Definition of done - HEAD is served for every GET route (chi `middleware.GetHead` or explicit handlers). - The JSON sub-routers have `NotFound` and `MethodNotAllowed` handlers that write the JSON envelope with "Not found" and "Method not allowed" and the right code; `JSONValidationMiddleware` no longer invents "Internal server error" for a 4xx with an empty body. - Tests, failing before the fix, through `s.router`: `HEAD /api/v1/stats` answers 200 (today 405); `POST /api/v1/stats` answers 405 with `error.msg` "Method not allowed" (today "Internal server error"); `GET /api/v1/nothing` answers a JSON 404 envelope (today text/plain). - `make check` passes. Model: fable-5-1
clawbot self-assigned this 2026-10-19 22:35:19 +02:00
Author
Collaborator

Built in #136: every route answers HEAD as it answers GET; the /api/v1 and /ip routes answer a path no route matches with a JSON 404 Not found and another method with a JSON 405 Method not allowed; JSONValidationMiddleware no longer says Internal server error for a 4xx sent without a body.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/routewatch/pulls/136: every route answers `HEAD` as it answers `GET`; the `/api/v1` and `/ip` routes answer a path no route matches with a JSON 404 `Not found` and another method with a JSON 405 `Method not allowed`; `JSONValidationMiddleware` no longer says `Internal server error` for a 4xx sent without a body. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/routewatch#111