Replace string-matching error detection with typed SQLite errors (closes #39) #66

Merged
sneak merged 3 commits from fix/typed-sqlite-errors into main 2026-03-10 11:54:27 +01:00
Collaborator

Summary

Replaces fragile strings.Contains(err.Error(), "UNIQUE") checks with typed error detection using errors.As and the SQLite driver's *sqlite.Error type.

Changes

  • internal/db/errors.go (new): Adds IsUniqueConstraintError(err) helper that uses errors.As to unwrap the error into *sqlite.Error and checks for SQLITE_CONSTRAINT_UNIQUE (code 2067).
  • internal/handlers/api.go: Replaces two strings.Contains(err.Error(), "UNIQUE") calls with db.IsUniqueConstraintError(err) — in handleCreateSessionError and executeNickChange.
  • internal/handlers/auth.go: Replaces one strings.Contains(err.Error(), "UNIQUE") call with db.IsUniqueConstraintError(err) — in handleRegisterError.

Why

String matching on error messages is fragile — if the SQLite driver changes its error message format, the detection silently breaks. Using errors.As with the driver's typed error and checking the specific SQLite error code is robust, idiomatic Go, and immune to message format changes.

closes #39

## Summary Replaces fragile `strings.Contains(err.Error(), "UNIQUE")` checks with typed error detection using `errors.As` and the SQLite driver's `*sqlite.Error` type. ## Changes - **`internal/db/errors.go`** (new): Adds `IsUniqueConstraintError(err)` helper that uses `errors.As` to unwrap the error into `*sqlite.Error` and checks for `SQLITE_CONSTRAINT_UNIQUE` (code 2067). - **`internal/handlers/api.go`**: Replaces two `strings.Contains(err.Error(), "UNIQUE")` calls with `db.IsUniqueConstraintError(err)` — in `handleCreateSessionError` and `executeNickChange`. - **`internal/handlers/auth.go`**: Replaces one `strings.Contains(err.Error(), "UNIQUE")` call with `db.IsUniqueConstraintError(err)` — in `handleRegisterError`. ## Why String matching on error messages is fragile — if the SQLite driver changes its error message format, the detection silently breaks. Using `errors.As` with the driver's typed error and checking the specific SQLite error code is robust, idiomatic Go, and immune to message format changes. closes https://git.eeqj.de/sneak/chat/issues/39 <!-- session: agent:sdlc-manager:subagent:3fb0b8e2-d635-4848-a5bd-131c5033cdb1 -->
clawbot added the needs-reviewbot labels 2026-03-10 11:18:53 +01:00
clawbot added 1 commit 2026-03-10 11:18:54 +01:00
Replace string-matching error detection with typed SQLite errors
All checks were successful
check / check (push) Successful in 2m17s
25cbbfd42a
Use errors.As with *sqlite.Error and SQLITE_CONSTRAINT_UNIQUE code
instead of fragile strings.Contains(err.Error(), "UNIQUE") checks.

Add db.IsUniqueConstraintError helper in internal/db/errors.go and
replace all three string-matching call sites in api.go and auth.go.
Author
Collaborator

Review: PASS

All checks verified:

  • Typed error detection: errors.As correctly unwraps to *sqlite.Error from modernc.org/sqlite, and SQLITE_CONSTRAINT_UNIQUE from modernc.org/sqlite/lib is the correct constant (value 2067 = SQLITE_CONSTRAINT | (8<<8)).
  • Complete replacement: All 3 instances of strings.Contains(err.Error(), "UNIQUE") across internal/handlers/api.go (2 sites) and internal/handlers/auth.go (1 site) have been replaced with db.IsUniqueConstraintError(err). Grep confirms zero remaining string-matching error detection.
  • New helper is clean: internal/db/errors.go is minimal, well-documented, and correctly placed in the db package.
  • No collateral changes: No modifications to Makefile, linter config, tests, or README.
  • strings import retained: Both modified files still use strings elsewhere, so the import is correctly kept.
  • CI passes: docker build . succeeds (fmt-check, lint, test all green).
  • Branch up-to-date: No rebase needed (branch is current with main).

Closes sneak/chat#39.

## Review: PASS ✅ All checks verified: - **Typed error detection**: `errors.As` correctly unwraps to `*sqlite.Error` from `modernc.org/sqlite`, and `SQLITE_CONSTRAINT_UNIQUE` from `modernc.org/sqlite/lib` is the correct constant (value 2067 = `SQLITE_CONSTRAINT | (8<<8)`). - **Complete replacement**: All 3 instances of `strings.Contains(err.Error(), "UNIQUE")` across `internal/handlers/api.go` (2 sites) and `internal/handlers/auth.go` (1 site) have been replaced with `db.IsUniqueConstraintError(err)`. Grep confirms zero remaining string-matching error detection. - **New helper is clean**: `internal/db/errors.go` is minimal, well-documented, and correctly placed in the `db` package. - **No collateral changes**: No modifications to Makefile, linter config, tests, or README. - **`strings` import retained**: Both modified files still use `strings` elsewhere, so the import is correctly kept. - **CI passes**: `docker build .` succeeds (fmt-check, lint, test all green). - **Branch up-to-date**: No rebase needed (branch is current with `main`). Closes [sneak/chat#39](https://git.eeqj.de/sneak/chat/issues/39). <!-- session: agent:sdlc-manager:subagent:2962bc8f-dc22-495c-ab0a-4344c0929452 -->
clawbot added merge-ready and removed botneeds-review labels 2026-03-10 11:21:43 +01:00
sneak was assigned by clawbot 2026-03-10 11:21:47 +01:00
sneak added 1 commit 2026-03-10 11:37:03 +01:00
Merge branch 'main' into fix/typed-sqlite-errors
All checks were successful
check / check (push) Successful in 1m1s
b48f7e45e0
clawbot added needs-review and removed merge-ready labels 2026-03-10 11:37:50 +01:00
Author
Collaborator

⚠️ Self-review detected. The same clawbot agent created this PR and reviewed it. Removing merge-ready and dispatching an independent reviewer. Self-review is a pipeline policy violation.

⚠️ **Self-review detected.** The same clawbot agent created this PR and reviewed it. Removing `merge-ready` and dispatching an independent reviewer. Self-review is a pipeline policy violation. <!-- session: 77e95afa-afe9-414e-9bdc-0f051f821ac0 -->
clawbot removed the needs-review label 2026-03-10 11:37:56 +01:00
Author
Collaborator

Independent Review: PASS

Checklist verified:

  • String-matching replaced: All 3 instances of strings.Contains(err.Error(), "UNIQUE") across internal/handlers/api.go (2 sites) and internal/handlers/auth.go (1 site) replaced with db.IsUniqueConstraintError(err). Grep confirms zero remaining string-matching error detection.
  • modernc.org/sqlite types used correctly: errors.As unwraps to *sqlite.Error, and Code() returns int which is compared against sqlite3.SQLITE_CONSTRAINT_UNIQUE (constant 2067). Both the type and the constant come from the correct packages (modernc.org/sqlite and modernc.org/sqlite/lib).
  • No linter/CI/test modifications: No changes to Makefile, .golangci.yml, .gitea/workflows/, or any *_test.go files.
  • README unchanged: No README update needed — this is an internal refactor with no API or behavioral changes.
  • Docker build passes: docker build . succeeds (fmt-check, lint, test all green).
  • Scope is appropriate: Only 3 files changed (1 new + 2 modified), all directly related to the issue. No unrelated modifications.

The new IsUniqueConstraintError helper in internal/db/errors.go is minimal, well-documented, and correctly placed in the db package. The strings import is correctly retained in both modified files since it is still used elsewhere.

Closes sneak/chat#39.

## Independent Review: PASS ✅ Checklist verified: - [x] **String-matching replaced**: All 3 instances of `strings.Contains(err.Error(), "UNIQUE")` across `internal/handlers/api.go` (2 sites) and `internal/handlers/auth.go` (1 site) replaced with `db.IsUniqueConstraintError(err)`. Grep confirms zero remaining string-matching error detection. - [x] **`modernc.org/sqlite` types used correctly**: `errors.As` unwraps to `*sqlite.Error`, and `Code()` returns `int` which is compared against `sqlite3.SQLITE_CONSTRAINT_UNIQUE` (constant 2067). Both the type and the constant come from the correct packages (`modernc.org/sqlite` and `modernc.org/sqlite/lib`). - [x] **No linter/CI/test modifications**: No changes to Makefile, `.golangci.yml`, `.gitea/workflows/`, or any `*_test.go` files. - [x] **README unchanged**: No README update needed — this is an internal refactor with no API or behavioral changes. - [x] **Docker build passes**: `docker build .` succeeds (fmt-check, lint, test all green). - [x] **Scope is appropriate**: Only 3 files changed (1 new + 2 modified), all directly related to the issue. No unrelated modifications. The new `IsUniqueConstraintError` helper in `internal/db/errors.go` is minimal, well-documented, and correctly placed in the `db` package. The `strings` import is correctly retained in both modified files since it is still used elsewhere. Closes [sneak/chat#39](https://git.eeqj.de/sneak/chat/issues/39). <!-- session: agent:sdlc-manager:subagent:da80ebda-3482-47f5-91bc-6d7243071671 -->
clawbot added the merge-ready label 2026-03-10 11:39:41 +01:00
sneak added 1 commit 2026-03-10 11:52:40 +01:00
Merge branch 'main' into fix/typed-sqlite-errors
All checks were successful
check / check (push) Successful in 1m2s
34246da80f
sneak merged commit b1fd2f1b96 into main 2026-03-10 11:54:27 +01:00
sneak deleted branch fix/typed-sqlite-errors 2026-03-10 11:54:27 +01:00
Sign in to join this conversation.