Skip to content

Releases: n-ae/svcerr

v1.0.6

Choose a tag to compare

@n-ae n-ae released this 18 Jul 21:27

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

Choose a tag to compare

@n-ae n-ae released this 18 Jul 21:18

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:

  • isNilValue only recognized reflect.Pointer as a nil-capable kind, so
    while svcerr's own error types (all pointer-backed) were safe, a
    non-pointer custom Coder or StackTracer holding a nil concrete value
    • a named nil slice, map, func, chan, or interface - still reached its
      own method and panicked instead of degrading to INTERNAL_ERROR/no-stack
      the way a typed-nil pointer already did. Coder and StackTracer are
      documented open extension interfaces with no pointer-receiver
      requirement, so this was a real gap in the boundary the package
      otherwise guarantees. isNilValue now switches over every nil-capable
      reflect.Kind (Chan, Func, Interface, Map, Pointer, Slice)
      and returns IsNil() for each.
  • The isNilValue doc 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

Choose a tag to compare

@n-ae n-ae released this 18 Jul 16:08

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 *NotFoundError returned nil on some path, then
    assigned to a plain error), but GetStackTrace and
    RecaptureStackTrace shared no such guard - each ran its own
    errors.As search and called the result directly, panicking on the
    same typed-nil-interface footgun outermostCoded already closed for
    classification. Since errorLogFields calls GetStackTrace for any
    5xx classification (exactly what a typed-nil error normalizes to),
    this reached WriteHTTPError/WriteHTTPProblem during logging,
    after the response body had already been written. Verified
    end-to-end against a real httptest.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 share outermostCoded's isNilValue guard, treating a typed-nil
    match as "not found" rather than dereferencing it.
  • logError now returns immediately when the logger is nil, before
    building log fields, matching the early-return pattern Renderer.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

Choose a tag to compare

@n-ae n-ae released this 18 Jul 14:32

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 *NotFoundError returned nil on some
    path, then assigned to a plain error) panicked response rendering
    instead of classifying as ErrCodeInternal. errors.As matches on
    type, not nilness, so the classic Go typed-nil-interface footgun
    reached Code() on a nil receiver before any accessor ran.
    outermostCoded now rejects a typed-nil match centrally, so
    GetErrorCode and every JSON/problem body writer that shares it
    degrade to ErrCodeInternal instead of dereferencing.
  • An external Coder-only implementation (a documented, first-class
    extension point that doesn't require embedding BaseError) could
    return "" from Code() and have it ride straight through to the
    wire as the machine-readable error code. New/Wrap already
    normalized this for the package's own constructors as of v1.0.2;
    GetErrorCode now 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
    Authenticator now state their contract explicitly: a true bool
    return implies a non-empty string, the same guarantee BaseError'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: Logger has no context.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

Choose a tag to compare

@n-ae n-ae released this 18 Jul 11:34

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_status is no longer reported for these two cases since no
    error response was actually rendered, and response_committed_status
    / hijacked remain the transport-level truth in its place.
  • safeJSONMarshal now validates that a nil-error json.Marshal
    result is actually valid JSON. Under legacy GODEBUG=panicnil=1
    semantics, a custom MarshalJSON that does a literal panic(nil)
    was invisible to recover(), so json.Marshal could return a
    truncated, invalid body with a nil error - silently reported as a
    fully successful response.
  • A marshaler panic carrying an error value is now wrapped with %w
    instead of flattened with %v, so errors.Is/errors.As can still
    reach it through RenderErr.
  • 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/Wrap normalize an empty ErrorCode to ErrCodeInternal
    instead of letting "" ride through to the wire as the
    machine-readable classification key. RegisterStatusCode and
    RendererConfig.StatusCodes now 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.0 instead 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

Choose a tag to compare

@n-ae n-ae released this 18 Jul 02:11

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

Choose a tag to compare

@n-ae n-ae released this 18 Jul 21:28

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/svcerr requirement: 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's reflect.Kind coverage from v1.0.5's fix - dedicated
    nil map, chan, and func Coder repros alongside the existing slice
    case. No source or behavior change.

Test coverage: 100.0%.

zerologadapter/v1.0.3

Choose a tag to compare

@n-ae n-ae released this 18 Jul 21:20

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/svcerr requirement: 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: isNilValue now recognizes every nil-capable reflect.Kind
    (Chan, Func, Interface, Map, Pointer, Slice), not just
    Pointer, so a non-pointer custom Coder or StackTracer holding a
    nil concrete value degrades safely to INTERNAL_ERROR/no-stack
    instead of panicking inside its own method.

Test coverage: 100.0%.

zerologadapter/v1.0.2

Choose a tag to compare

@n-ae n-ae released this 18 Jul 16:37

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/svcerr requirement: 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: GetStackTrace and RecaptureStackTrace now guard against a
    typed-nil coded error the same way GetErrorCode already did, so
    logging a typed-nil error (e.g. a nil *NotFoundError assigned to a
    plain error) no longer panics after the response body has already
    been written; logError also now returns immediately on a nil
    logger.

Test coverage: 100.0%.

zerologadapter/v1.0.1

Choose a tag to compare

@n-ae n-ae released this 18 Jul 15:13

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/svcerr requirement: 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; safeJSONMarshal validates nil-error
    json.Marshal results; 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 *NotFoundError assigned
    to a plain error) no longer panics response rendering; an external
    Coder's empty Code() is now normalized the same way New/Wrap
    already were.

Test coverage: 100.0%.