Releases: n-ae/svcerr
Release list
v1.0.6
Test-only release: no source or behavior change from v1.0.5.
Adds regression tests rounding out isNilValue's reflect.Kind
coverage from v1.0.5's fix. TestGetErrorCodeWithNilNonPointerCoder
and its StackTracer sibling already exercised the slice arm (and
TestGetErrorCodeWithNonNilCapableValueCoder the default branch),
giving 100% line coverage; this adds dedicated nil map, chan, and func
Coder repros so each reachable nil-capable kind has its own named
test, not just incidental line coverage via the slice case. Each new
test panics on the pre-fix path (map write, chan close, func call) if
the isNilValue guard ever regresses.
reflect.Interface remains in isNilValue's switch to match
reflect.Value.IsNil's documented kind set, but is unreachable through
this package's actual call sites (GetErrorCode/GetStackTrace/
RecaptureStackTrace all convert an interface-typed local to any
first, which Go always flattens to the underlying concrete type) -
documented as defensive/unreachable rather than given a test that
cannot exercise it.
No breaking changes.
Test coverage: 100.0%.
v1.0.5
Patch release closing the L1 finding from
docs/assessment-maintainable-architect-v4-v1.0.4.md,
which reconciled two independent reviews of v1.0.4 (a Codex direct review
and a second third-party review) through fresh verification against actual
source. No breaking changes; module path unchanged.
Fixed:
isNilValueonly recognizedreflect.Pointeras a nil-capable kind, so
whilesvcerr's own error types (all pointer-backed) were safe, a
non-pointer customCoderorStackTracerholding a nil concrete value- a named nil slice, map, func, chan, or interface - still reached its
own method and panicked instead of degrading toINTERNAL_ERROR/no-stack
the way a typed-nil pointer already did.CoderandStackTracerare
documented open extension interfaces with no pointer-receiver
requirement, so this was a real gap in the boundary the package
otherwise guarantees.isNilValuenow switches over every nil-capable
reflect.Kind(Chan,Func,Interface,Map,Pointer,Slice)
and returnsIsNil()for each.
- a named nil slice, map, func, chan, or interface - still reached its
- The
isNilValuedoc comment's claim that no error type in practice uses
the non-pointer kinds is removed; it was disproved by the reviews'
own reproduction.
Regression tests added for a nil-valued non-pointer Coder, the
StackTracer equivalent, and a non-nil-capable value Coder (a struct),
covering both the newly-guarded nil branch and the unaffected not-nil
default branch.
Test coverage: 100.0% (root module). zerologadapter is unchanged this
release - its go.mod already requires svcerr v1.0.4, and this is a
non-breaking patch, so no adapter bump is needed.
v1.0.4
Patch release closing the M1 finding from
docs/assessment-maintainable-architect-v4-v1.0.3.md,
which reconciled two independent reviews of v1.0.3 (a Codex direct review
and a second, adversarially-minded review) through fresh verification
against actual source rather than trusting either review's prose. No
breaking changes; module path unchanged.
Fixed:
- v1.0.3 protected classification (
GetErrorCode) against a typed-nil
coded error (e.g. a*NotFoundErrorreturned nil on some path, then
assigned to a plainerror), butGetStackTraceand
RecaptureStackTraceshared no such guard - each ran its own
errors.Assearch and called the result directly, panicking on the
same typed-nil-interface footgunoutermostCodedalready closed for
classification. SinceerrorLogFieldscallsGetStackTracefor any
5xx classification (exactly what a typed-nil error normalizes to),
this reachedWriteHTTPError/WriteHTTPProblemduring logging,
after the response body had already been written. Verified
end-to-end against a realhttptest.Server: inside
RecoveryMiddleware, that second panic was treated as arriving after
response commitment, aborting the connection and dropping an
already-correct 500 response instead of delivering it. Both functions
now shareoutermostCoded'sisNilValueguard, treating a typed-nil
match as "not found" rather than dereferencing it. logErrornow returns immediately when the logger isnil, before
building log fields, matching the early-return patternRenderer.log
already used one layer up - avoids doing (and risking) work for a
caller who opted out of logging entirely.
Test coverage: 100.0% (root module). zerologadapter is unchanged this
release - per the documented adapter-versioning policy, it's tagged
independently and only when the adapter itself changes, so its latest
tag lagging the root's is expected, not an oversight.
v1.0.3
Patch release closing the three actionable findings from
docs/assessment-maintainable-architect-v4-v1.0.2.md, which reconciled
two disagreeing Codex reviews of v1.0.2 (one reporting zero findings,
one reporting five) through independent adversarial verification. No
breaking changes; module path unchanged.
Fixed:
- A typed-nil coded error (e.g. a
*NotFoundErrorreturned nil on some
path, then assigned to a plainerror) panicked response rendering
instead of classifying asErrCodeInternal.errors.Asmatches on
type, not nilness, so the classic Go typed-nil-interface footgun
reachedCode()on a nil receiver before any accessor ran.
outermostCodednow rejects a typed-nil match centrally, so
GetErrorCodeand every JSON/problem body writer that shares it
degrade toErrCodeInternalinstead of dereferencing. - An external
Coder-only implementation (a documented, first-class
extension point that doesn't require embeddingBaseError) could
return""fromCode()and have it ride straight through to the
wire as the machine-readable error code.New/Wrapalready
normalized this for the package's own constructors as of v1.0.2;
GetErrorCodenow normalizes it too, closing the gap for bare
third-party types at the one extraction point every renderer funnels
through.
Documented:
Coder,ProblemTyper,ProblemInstancer,ProblemTitler, and
Authenticatornow state their contract explicitly: atruebool
return implies a non-empty string, the same guaranteeBaseError's
own implementations already make. No code change - a third-party
implementation following the documented convention was already safe.- Two design choices raised by the v1.0.2 review and confirmed
deliberate rather than oversights are now recorded as ADRs instead of
living only in review prose:Loggerhas nocontext.Context
parameter (docs/adr/0001),
and a marshal-panic log keeps the original error's stack rather than
capturing a second one at the panic site
(docs/adr/0002).
Test coverage: 100.0% (root module), 100.0% (zerologadapter, unchanged
this release).
v1.0.2
Patch release closing the v1.0.2 priority list from
docs/assessment-maintainable-architect-v4-head-2026-07-18.md, a
cross-review of v1.0.1 + post-tag HEAD combining an external review and
an independent Codex direct review
(docs/repository-assessment-head-codex-2026-07-18.md). No breaking
changes; module path unchanged.
Fixed:
- Panic-recovery log fields (stack trace included) were dropped for a
panic recovered after the response was already committed, or after a
successful Hijack, because field selection was keyed off the stale
HTTP status of the prior response rather than the panic itself.
Fields are now derived from the recovered error's own severity;
http_statusis no longer reported for these two cases since no
error response was actually rendered, andresponse_committed_status
/hijackedremain the transport-level truth in its place. safeJSONMarshalnow validates that a nil-errorjson.Marshal
result is actually valid JSON. Under legacyGODEBUG=panicnil=1
semantics, a customMarshalJSONthat does a literalpanic(nil)
was invisible torecover(), sojson.Marshalcould return a
truncated, invalid body with a nil error - silently reported as a
fully successful response.- A marshaler panic carrying an
errorvalue is now wrapped with%w
instead of flattened with%v, soerrors.Is/errors.Ascan still
reach it throughRenderErr. - RFC 9457 problem titles no longer render empty for a nonstandard
status (e.g. a custom code mapped to 499):http.StatusText
returning""now falls back to the package's own occurrence-
invariant per-code text, matching the JSON/HTML fallback bodies.
Changed:
New/Wrapnormalize an emptyErrorCodetoErrCodeInternal
instead of letting""ride through to the wire as the
machine-readable classification key.RegisterStatusCodeand
RendererConfig.StatusCodesnow reject an empty code key at
registration.- Both GitHub Actions workflows declare
permissions: contents: read
explicitly rather than relying on the mutable repository-level
default, and the vulnerability-scan workflow pins
govulncheck@v1.6.0instead of floating@latest, so a future scan
result stays reproducible against a fixed scanner version.
Test coverage: 100.0% (root module), 100.0% (zerologadapter).
v1.0.1
Patch release implementing the v1.0.0 assessment's priority list
(docs/assessment-maintainable-architect-v4-v1.0.0.md, with
docs/direct-repository-assessment-v1.0.0-2026-07-17.md and an external
review of the tagged release as convergent inputs). No breaking changes;
module path unchanged.
Fixed:
- The JSON/problem+json marshal-failure fallback is a complete
reclassification to ErrCodeInternal. Its status now comes from the
active mapping - a Renderer's StatusCodes override or the
RegisterStatusCode registry - instead of a hard-coded 500, so a
deployment that maps INTERNAL_ERROR to 503 gets 503 on fallback
responses too. Headers derived from the original error's
classification (Retry-After, an error-specific WWW-Authenticate
challenge) no longer survive onto the fallback; the default challenge
still applies when the mapped fallback status is 401, exactly as it
would for a bare internal error under that mapping. - A caller-supplied json.Marshaler that panics (attached via
SetPublicDetail) is recovered into a RenderErr plus the standard
fallback body instead of escaping the response writers - the same
containment policy safeLog already applies to panicking Loggers.
Changed:
- A panic recovered after a successful Hijack is logged as its own
variant ("Panic recovered in HTTP handler after connection was
hijacked") with hijacked=true, and without the misleading
response_committed_status=0 and http_status=0 fields. Behavior is
otherwise unchanged: recovery still writes nothing to a hijacked
connection and still does not close it - after Hijack the handler
owns the connection.
Documentation:
- Identity fixation is documented as by-reference, not deep copy: a
mutable object passed to a constructor stays shared and later
mutation is visible through Value() and Context(). This never affects
wire output or logs (package doc, Value(), README). - The README recovery section documents hijacked-connection ownership
and the defer conn.Close() pattern. - The README status-mapping link points at status.go (http.go was
removed in the v0.11.0 split); remaining interface{} snippet
spellings migrated to any; logger.go's nil-err example replaced with
an actual call path. - zerologadapter's Log signature spells map[string]any (type-identical;
no adapter release is required for it).
Test coverage: 100.0% (root module), 100.0% (zerologadapter).
zerologadapter/v1.0.4
Dependency-only release: the adapter's own API and behavior are
unchanged. go.mod's github.com/n-ae/svcerr requirement moves from
v1.0.5 to v1.0.6. No breaking changes; module path unchanged
(github.com/n-ae/svcerr/zerologadapter).
Changed:
github.com/n-ae/svcerrrequirement: v1.0.5 → v1.0.6.
Root-module change now in range (doesn't touch any surface this
adapter uses - New, wrapping a zerolog.Logger into svcerr's
Logger interface):
- v1.0.6: test-only release. Adds regression tests rounding out
isNilValue'sreflect.Kindcoverage from v1.0.5's fix - dedicated
nil map, chan, and funcCoderrepros alongside the existing slice
case. No source or behavior change.
Test coverage: 100.0%.
zerologadapter/v1.0.3
Dependency-only release: the adapter's own API and behavior are
unchanged. go.mod's github.com/n-ae/svcerr requirement moves from
v1.0.4 to v1.0.5. No breaking changes; module path unchanged
(github.com/n-ae/svcerr/zerologadapter).
Changed:
github.com/n-ae/svcerrrequirement: v1.0.4 → v1.0.5.
Root-module change now in range (doesn't touch any surface this
adapter uses - New, wrapping a zerolog.Logger into svcerr's
Logger interface):
- v1.0.5:
isNilValuenow recognizes every nil-capablereflect.Kind
(Chan,Func,Interface,Map,Pointer,Slice), not just
Pointer, so a non-pointer customCoderorStackTracerholding a
nil concrete value degrades safely toINTERNAL_ERROR/no-stack
instead of panicking inside its own method.
Test coverage: 100.0%.
zerologadapter/v1.0.2
Dependency-only release: the adapter's own API and behavior are
unchanged. go.mod's github.com/n-ae/svcerr requirement moves from
v1.0.3 to v1.0.4. No breaking changes; module path unchanged
(github.com/n-ae/svcerr/zerologadapter).
Changed:
github.com/n-ae/svcerrrequirement: v1.0.3 → v1.0.4.
Root-module change now in range (doesn't touch any surface this
adapter uses - New, wrapping a zerolog.Logger into svcerr's
Logger interface):
- v1.0.4:
GetStackTraceandRecaptureStackTracenow guard against a
typed-nil coded error the same wayGetErrorCodealready did, so
logging a typed-nil error (e.g. a nil*NotFoundErrorassigned to a
plainerror) no longer panics after the response body has already
been written;logErroralso now returns immediately on a nil
logger.
Test coverage: 100.0%.
zerologadapter/v1.0.1
Dependency-only release: the adapter's own API and behavior are
unchanged. go.mod's github.com/n-ae/svcerr requirement moves from
v1.0.0 to v1.0.3, picking up the root module's three most recent patch
releases. No breaking changes; module path unchanged
(github.com/n-ae/svcerr/zerologadapter).
Changed:
github.com/n-ae/svcerrrequirement: v1.0.0 → v1.0.3.
Root-module changes now in range (none touch any surface this adapter
uses - New, wrapping a zerolog.Logger into svcerr's Logger
interface):
- v1.0.1: closed the v1.0.0 review's findings (committed/hijacked
panic-recovery log fields, marshaler-panic containment). - v1.0.2: panic-recovery log fields now derive from the recovered
error's own severity;safeJSONMarshalvalidates nil-error
json.Marshalresults; marshaler-panic errors preserve identity via
%w; empty error codes are normalized at construction (New/Wrap)
and empty status-registration keys are rejected; RFC 9457 titles fall
back correctly for nonstandard statuses. - v1.0.3: a typed-nil coded error (e.g. a nil
*NotFoundErrorassigned
to a plainerror) no longer panics response rendering; an external
Coder's emptyCode()is now normalized the same wayNew/Wrap
already were.
Test coverage: 100.0%.