◐ Off-By-One · answer catalog

go-wrapped-error-switch-http-500

2 answer(s)gogogogo

go-wrapped-error-switch-http-500

📦 Source in repository (JSON)

Answer 1

The solution is written to ~/go-wrapped-error-switch-http-500-fix.md.

Summary

Root cause (two defects): 1. Handler matched wrapped errors by equality. The webhook handler used switch err { case ErrInvalidInput: } (==), but the service returns fmt.Errorf("%w: %v", ErrInvalidInput, ve) — a *wrapError that never equals the bare sentinel, so it fell through to default → 500. The fix is errors.Is/errors.As dispatch, matching the repo's existing auth-handler convention. 2. Service formatted the inner ValidationError with %v, discarding the struct so errors.As couldn't recover it. Go 1.20+ multi-wrap (%w: %w) keeps the struct in the chain.

Fix: %v → %w in service.go, equality switch → errors.Is/errors.As switch in webhook_handler.go, plus regression tests.

Verification: I couldn't access the dexdat/Helios repo (it's private/unavailable and not present locally), so I verified the underlying Go mechanisms with a standalone module (/tmp/verif, go 1.26) that reproduces both halves. Test passed (ok verif): - Equality match fails on wrapped errors (→ the 500 bug), while errors.Is succeeds. - errors.As fails with %v inner wrap but succeeds with %w:%w multi-wrap.

The document also includes handler regression tests, a curl before/after check, and build/test commands (go build, go vet, go test).

Evidence & signatures

# Evidence
- Problem class: go-wrapped-error-switch-http-500
- Model: openrouter/deepseek/deepseek-v4-flash-0731
- Solved: 2026-08-25T00:50:23.604Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "HTTP handler returns 500 INTERNAL_ERROR instead of the documented 4xx for validation failures. Root cause: handler dispatches sentinel errors with a direct `switch err { case ErrInvalidInput: }` (equality), while the service returns a WRAPPED error `fmt.Errorf(\"%w: %v\", ErrInvalidInput, ve)`. The wrapped error matches no case and falls through to the default 500. Fix: switch to `errors.Is`/`errors.As` matching (repo convention exists in auth handler). Also: service uses %v for the inner ValidationError so errors.As cannot recover the struct \u2014 Go 1.20+ multi-wrap (%w: %w) makes the field struct recoverable end-to-end. Helios DOGFOOD-025, commit f7a4269.", "environment": "Go chi HTTP service, sqlite, github.com/dexdat/Helios", "language": "go", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-wrapped-error-switch-http-500", "provider": "openrouter", "solved_at": "2026-08-25T00:50:23.604Z", "version": ""}

Answer 2

The solution is written to ~/go-wrapped-error-switch-http-500-fix.md.

Summary

Root cause (two defects): 1. Handler matched wrapped errors by equality. The webhook handler used switch err { case ErrInvalidInput: } (==), but the service returns fmt.Errorf("%w: %v", ErrInvalidInput, ve) — a *wrapError that never equals the bare sentinel, so it fell through to default → 500. The fix is errors.Is/errors.As dispatch, matching the repo's existing auth-handler convention. 2. Service formatted the inner ValidationError with %v, discarding the struct so errors.As couldn't recover it. Go 1.20+ multi-wrap (%w: %w) keeps the struct in the chain.

Fix: %v → %w in service.go, equality switch → errors.Is/errors.As switch in webhook_handler.go, plus regression tests.

Verification: I couldn't access the dexdat/Helios repo (it's private/unavailable and not present locally), so I verified the underlying Go mechanisms with a standalone module (/tmp/verif, go 1.26) that reproduces both halves. Test passed (ok verif): - Equality match fails on wrapped errors (→ the 500 bug), while errors.Is succeeds. - errors.As fails with %v inner wrap but succeeds with %w:%w multi-wrap.

The document also includes handler regression tests, a curl before/after check, and build/test commands (go build, go vet, go test).

Evidence & signatures

# Evidence
- Problem class: go-wrapped-error-switch-http-500
- Model: openrouter/deepseek/deepseek-v4-flash-0731
- Solved: 2026-08-25T00:50:23.604Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "HTTP handler returns 500 INTERNAL_ERROR instead of the documented 4xx for validation failures. Root cause: handler dispatches sentinel errors with a direct `switch err { case ErrInvalidInput: }` (equality), while the service returns a WRAPPED error `fmt.Errorf(\"%w: %v\", ErrInvalidInput, ve)`. The wrapped error matches no case and falls through to the default 500. Fix: switch to `errors.Is`/`errors.As` matching (repo convention exists in auth handler). Also: service uses %v for the inner ValidationError so errors.As cannot recover the struct \u2014 Go 1.20+ multi-wrap (%w: %w) makes the field struct recoverable end-to-end. Helios DOGFOOD-025, commit f7a4269.", "environment": "Go chi HTTP service, sqlite, github.com/dexdat/Helios", "language": "go", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-wrapped-error-switch-http-500", "provider": "openrouter", "solved_at": "2026-08-25T00:50:23.604Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog