error-handling
Technical notes on web development, DevOps, and AI integration.
11 articles
- 00:24backend
The Column Width Belongs in the Validator, Not in MySQL
Length validation must mirror the column width, not leave it to the database. When 31 characters reach the query, MySQL strict mode answers 500.
TL;DR: The KotaPortal admin form accepted 31 characters but threw a 500 error. The tipe_cabor column was declared VARCHAR(30). Validation was added to prevent this error.
#go#validation#api - 16:02backend
400 or 404: the Right Error Status from a Go Service Layer
A Create with an unknown category_slug returns 400, not 404: sentinel errors separate bad input values from missing resources.
TL;DR: HTTP 400 covers unusable requests. HTTP 404 describes absent resources. Error handling uses sentinels.
#go#error-handling#validation - 12:58backend
A Deleted User Is an Authentication Failure: 401, Not 500
A cryptographically valid JWT can still name a user who no longer exists. Mapping sql.ErrNoRows to 401 lets the frontend purge the stale session.
TL;DR: A deleted user's JWT kept hitting the API, and since the middleware treated missing rows as a generic DB error, users got stuck in a 500 banner loop instead of being logged out. The fix uses errors.Is(err, sql.ErrNoRows) to return 401, letting the frontend purge the stale session. Tests now pin both paths.
#go#golang#jwt - 13:50backend
When a Generic Error Swallows the Validation Message
The service composed a precise validation message; the handler answered generically. A bridge pattern forwards audited static detail, locked by a regression test.
TL;DR: A shared respondErr helper collapsed a precise validation message into one generic string, leaving an admin guessing. The fix forwards static business-rule detail after auditing every wrap site for user input, while dynamic detail stays in logs. A regression test locks the message so it cannot silently vanish again.
#go#error-handling#api - 01:00backend
The filter that stayed silently empty
Three public failures in one night: a filter that never matched, search exploding to 500, and a 403 echoing input.
TL;DR: A broken anchored LIKE pattern made the amenity filter always return empty rows, fixed by switching to substring matching with wildcards escaped. An unsanitized search term crashed MySQL's full-text parser via a single asterisk, fixed by stripping operator characters at the boundary. A 403 response echoed user input back to attackers, fixed by replacing it with a static message while logging the raw value.
#mysql#sql#input-validation - 21:24backend
When the Guard Cannot Answer: Fail-Closed Media Deletion
A failing usage census used to count as permission to delete. Now ErrCensusUnavailable cancels the delete: row and file survive, client gets 503.
TL;DR: Media deletion used to fail open: if the usage-count query errored, the code assumed the file was safe to delete. The fix returns ErrCensusUnavailable, mapped to a 503 CENSUS_UNAVAILABLE response, with real details logged server-side per OWASP guidance. Tests now verify deletion is blocked, data stays intact, and no stray files are written.
#golang#error-handling#http - 21:06frontend
The One-Line Catch That Deleted Admin Sessions
An API outage and an expired session were treated the same by a single catch, until one server restart logged out every admin.
TL;DR: A single catch-all deleted the session token on any error, so a brief backend outage logged out every admin. The fix: classify failures first—only 401 purges the token, 403 shows an access-denied notice, and 5xx or network errors keep the session with a retry banner. Five unit tests lock the classification.
#javascript#http#error-handling - 16:24backend
An Undocumented 404 Is a Contract Bug
Six lines of OpenAPI YAML and a regeneration changed what the frontend compiler knows. Error responses left undocumented are contract bugs.
TL;DR: What looked like a trivial six-line OpenAPI cleanup turned out to be critical for type safety. Since openapi-typescript only generates types for explicitly documented responses, an undocumented 404 forced the frontend to guess or use any. Documenting the error locks the contract so TypeScript catches missing handling early, saving hours of debugging later.
#openapi#api#typescript - 16:24backend
When a Feature Is Turned Off, Answer 404 Not 403
A disabled reviews feature answered 404 instead of 403. The HTTP spec actually backs that choice, and one cheap gate makes it practical.
TL;DR: Returning 403 for a disabled feature confirms the resource exists, inviting attackers to keep probing. Using 404 instead hides it completely, and a single service-layer flag can protect all related endpoints while the frontend hides UI via feature flags. For admin APIs though, 403 or 410 makes more sense since admins need to know the feature is just disabled.
#http#api#error-handling