debugging
Technical notes on web development, DevOps, and AI integration.
15 articles
- 18:58backend
Admin Page 500 and the Count Query That Lost Its Join
A shared WHERE filter referenced an alias the count query never joined, so every admin list request died with a 500. The fix was one line.
TL;DR: A superadmin's review moderation page crashed with a 500 due to an unknown column error, despite the list API working fine. The shared filter referenced alias c.module, but the count query hadn't joined the categories table, so MySQL couldn't resolve it. Adding the missing LEFT JOIN fixed the count and restored pagination, highlighting that shared filters need matching joins.
#mysql#go#sql - 13:28backend
A Complaint Born Without a Trace
An empty complaint timeline was not a rendering bug: the first history row was never inserted, because twin functions CreateRegistration and CreateReport had drifted apart.
TL;DR: A new complaint had no status history because CreateReport never seeded the initial NULL-to-submitted row, while its twin CreateRegistration did. The fix wraps report creation in a transaction that inserts both the report and its opening history entry atomically. A new regression test now guards the behavior, highlighting the need to test twin functions side by side.
#golang#mysql#audit-trail - 13:00backend
Saving Without Changes Returns 404? RowsAffected Fooled Me
A no-op form save got a 404 because MySQL counts changed rows, not matched rows. The fix: an existence check only on the zero path.
TL;DR: Hitting Save without changes returned 404 because MySQL reports zero affected rows when nothing actually changes. The default driver setting means zero is ambiguous and doesn't reliably indicate a missing record. The fix adds a quick existence check on zero instead of changing the global DSN setting.
#mysql#go#debugging - 08:16backend
The One Forgotten Line: How a DTO Mapper Crashed the Frontend
A field existed on the entity but never made it into the response mapper: the API answered with an empty string and the edit page crashed. One line plus a regression test closed it.
TL;DR: Saving announcements crashed because the backend mapper forgot to copy Status, sending an empty string that broke the frontend badge lookup. The fix added the missing field, added a regression test, and made the UI whitelist statuses to default to draft. Navigation was also switched to router push and refresh to keep toast feedback without a full reload.
#golang#react#typescript - 15:26tooling
The Markdown converter I trust is the one that refuses
A two-way repo-to-Plane converter that rejects anything outside a closed subset, line number included, instead of guessing.
TL;DR: The doc-sync converter rejects unsupported Markdown with an exact line number instead of silently truncating content. It only handles a strict subset like headings to h4, lists, tables and code blocks while frontmatter stays repo-side and images become pointers. This strict grammar and repo-side normalization makes round-trip conversions predictable and errors easy to fix.
#plane#python#markdown - 09:16frontend
A Font Experiment That Ended in 22 Minutes
Reverting a heading font took one line because every heading reads a single token. Taste experiments can be cheap.
TL;DR: A font experiment on this project lasted twenty-two minutes: one commit swapped the heading token to Source Serif 4, the next reverted it. Because every heading reads a single CSS variable instead of hardcoded font names, the whole trial cost one line each way. The failed taste experiment left behind cheap reversibility and an untouched, durable type scale.
#css#tailwind#accessibility - 09:13frontend
Four Commits Chasing One Vanishing Border
A card border vanished on a muted background because two grays were near-twins. Tokens, contrast, and backdrop assumptions.
TL;DR: The missing borders were never broken code; the border token was nearly identical to the muted background, so the line was invisible. The fix used Tailwind's opacity modifier for a subtle primary-colored border plus a card tint, which also satisfies WCAG 1.4.11 contrast. Per-card icons and left alignment followed; now every component gets tested on every surface.
#css#tailwind#accessibility - 03:16frontend
The Gray Office Map: the MapLibre Worker That Never Loaded a Tile
Swapped a static map for MapLibre in Next.js 16. The frame rendered, zero tiles loaded, zero errors. The worker never started.
TL;DR: A MapLibre map rendered markers but gray tiles under Next.js 16's Turbopack, which fails to emit the worker's companion file, so zero tile requests fired and the console stayed clean. The fix: load MapLibre 4.7.1 from a CDN, where the worker inlines as a blob URL. Version 6+ instead recommends copying worker files to public and setting workerUrl.
#maplibre#openfreemap#nextjs - 20:47devops
The Launcher That Pays Twice: Three Guard Layers Before a Delegation Script Fires
Launching a delegation script twice burns the budget twice. flock, artifact check, and pgrep refuse before anything starts.
TL;DR: The author re-ran a Claude Code command thinking it was stuck, accidentally spawning a duplicate that burned the same budget in parallel. Their launcher now refuses to start using three pre-flight checks: flock -n on a file descriptor, a valid result JSON check, and pgrep -f for a live twin. Each refusal gets a distinct exit code.
#bash#devops#automation