Stop putting raw errors on screen (audit L-4) #146

Merged
qwc merged 1 commit from fix/user-facing-errors into main 2026-08-30 20:43:01 +02:00
Owner

Audit item L-4. Independent of #144 and #145.

What was showing

Handlers passed err.Error() straight into http.Error. Creating a user with a taken username answered the browser with:

Failed to create user: creating user: constraint failed: UNIQUE constraint failed: users.username (2067)

Driver text, table and column name, SQLite error code. Robot creation and the built-in docs deploy did the same, and the archive/PDF upload paths did it with filesystem errors — which carry absolute storage paths.

None of it helps the person reading it, and it describes the schema and the layout on disk to anyone who can reach an admin form.

What replaces it

Two helpers in internal/handler/errors.go split the audiences: userError for HTML endpoints, jsonUserError for the API. Both log the real error with context (project slug, username, version tag) and send the user a message saying what failed and what they can do — "the username or email may already be taken", "check that it is a supported format, within the size limits, and contains no absolute or parent paths".

The browser upload path renders a page rather than calling http.Error, so it gained an explicit log line instead.

One message stays verbatim, with a comment explaining why: an invalid version keep pattern reports the regular expression the admin just typed (missing closing )). That error describes their input, not our internals, and showing it is the point.

Tests

internal/handler/user_errors_test.go posts a duplicate username to the real admin endpoint and asserts the response says what failed while containing none of UNIQUE, constraint, sqlite, users.username, SQL. It fails on main with the string quoted above — I checked by stashing the fix rather than assuming.

Full suite green.


Assisted by Claude Opus 5.

Audit item **L-4**. Independent of #144 and #145. ## What was showing Handlers passed `err.Error()` straight into `http.Error`. Creating a user with a taken username answered the browser with: ``` Failed to create user: creating user: constraint failed: UNIQUE constraint failed: users.username (2067) ``` Driver text, table and column name, SQLite error code. Robot creation and the built-in docs deploy did the same, and the archive/PDF upload paths did it with filesystem errors — which carry absolute storage paths. None of it helps the person reading it, and it describes the schema and the layout on disk to anyone who can reach an admin form. ## What replaces it Two helpers in `internal/handler/errors.go` split the audiences: `userError` for HTML endpoints, `jsonUserError` for the API. Both log the real error with context (project slug, username, version tag) and send the user a message saying what failed and what they can do — "the username or email may already be taken", "check that it is a supported format, within the size limits, and contains no absolute or parent paths". The browser upload path renders a page rather than calling `http.Error`, so it gained an explicit log line instead. **One message stays verbatim**, with a comment explaining why: an invalid version keep pattern reports the regular expression the admin just typed (`missing closing )`). That error describes their input, not our internals, and showing it is the point. ## Tests `internal/handler/user_errors_test.go` posts a duplicate username to the real admin endpoint and asserts the response says what failed while containing none of `UNIQUE`, `constraint`, `sqlite`, `users.username`, `SQL`. It fails on `main` with the string quoted above — I checked by stashing the fix rather than assuming. Full suite green. --- Assisted by Claude Opus 5.
Stop putting raw errors on screen (audit L-4)
All checks were successful
CI / test (pull_request) Successful in 1m25s
CI / build (pull_request) Successful in 48s
CI / docker (pull_request) Has been skipped
8d3870cb68
Handlers passed err.Error() into http.Error, so a duplicate username
answered the browser with "constraint failed: UNIQUE constraint failed:
users.username (2067)" — driver text, table and column names included.
Archive and PDF failures did the same with filesystem errors, which
carry absolute storage paths.

A pair of helpers now splits the two audiences: the user gets a message
that says what failed and what they can do about it, the log keeps the
cause with the project or username that produced it.

One message stays verbatim, with a comment saying why: an invalid
version keep pattern reports the regular expression the admin just
typed, which is the point of showing it.

Assisted-by: Claude Opus 5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marcel M. Otte <marcel.otte@mmo.to>
qwc merged commit 56e4cb9cba into main 2026-08-30 20:43:01 +02:00
qwc deleted branch fix/user-facing-errors 2026-08-30 20:43:01 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
qwc-open/asiakirjat!146
No description provided.