Skip to content

Releases: repenguin22/errtrail

v1.3.2

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 13:12

Follow-up on the v1.3.1 review-round batch — two spots the sweep still
missed.

  • Fixed TestErrorStructSize (added in v1.3.1 to pin the 144 B layout
    fix) asserted 144 unconditionally, failing on any 32-bit target: uintptr
    and slice/string header words are 4 bytes there, and time.Duration
    (retryDelay) aligns to 4 rather than 8 — hand-computed and confirmed at
    80 B for GOARCH=386 (no 32-bit CI runner to run it on directly, but
    go vet/go test -c cross-compile cleanly). The test now branches on
    unsafe.Sizeof(uintptr(0)). Test-only; no library behavior changed on
    any platform.
  • Docs Three more enumerations of the client-visible channels missed
    the retry delay: DESIGN.md's WithoutPublic edge-case-table row, the
    README "at the source" intro paragraph, and detailed's (format.go)
    internal comment on what %+v gathers.

v1.3.1

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 13:01

External review round 7 findings.

  • Fixed NewSkip / WrapSkip with a skip large enough that 3+skip
    overflows int (e.g. math.MaxInt) recorded a bogus frame near the top
    of the call stack instead of the documented "unknown" fallback for a skip
    past the stack top. caller now guards the overflow explicitly.
  • Changed Error's allocation drops back to 144 B (was 160 B since
    v1.3.0): noPublicBelow moved right after code in the struct, filling
    its padding byte instead of adding one at the end. Pure layout — no
    behavior or API change. Pinned by a struct-size test; README/doc.go
    benchmarks and cost notes updated.
  • Changed LogValue no longer costs allocations for public fields and
    field violations it never emits — collect's internal signature gained
    an includePublic flag, false for LogValue, true for %+v. Same
    chain-walk count, just fewer discarded slices.
  • Docs The "exactly three channels" wording missed by the v1.3.0 sweep
    doc.go's package-overview bullet, DESIGN.md's header/struct
    sketch/test-plan lines, and an inspect_test.go comment — now says four.

v1.3.0

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 08:56

Additive API only — no behavior change for existing code; construction
allocation grows one size class (ROADMAP §5).

  • Added (*Error).WithRetryDelay(d) and LookupRetryDelay(err) — the
    fourth client-visible channel, carrying a per-error dynamic retry
    delay (a rate limiter's actual time to the next token) that
    grpcerr.ToStatus emits as the RetryInfo detail, beating the static
    registry delay (RetryAfter). Outermost delay wins; blocked below a
    WithoutPublic barrier; excluded from LogValue; shown as %+v's new
    public.retry: line. A non-positive delay is a no-op — the input is a
    computed value and "retry after zero" carries no recommendation
    (deliberately not a panic, unlike registration-time RetryAfter). The
    "exactly three channels" contract wording moves to four across the docs.
    problem deliberately still emits no Retry-After header — the README
    shows the two-line handler pattern via LookupRetryDelay.
  • Changed The Error struct grew by the delay field: construction is
    now 160 B (was 144 B), still ~200ns / 1 alloc. README benchmarks updated.

v1.2.0

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 08:29

Additive only — no behavior changes for existing code (ROADMAP §4).

  • Added NewSkip(skip, code, msg) and WrapSkip(skip, err, msg)
    caller-skip constructors for error factories (external review round 6). A
    helper like apperr.NotFound(msg) built on plain New records its own
    line in every trace; skip=1 points the frame at the factory's caller
    instead (zap AddCallerSkip precedent). Separate functions, so the
    New/Wrap hot path is unchanged. skip=0 is exactly New/Wrap; a
    skip past the top of the stack records the "unknown" frame; a negative
    skip panics. Core coverage reaches 100% — the over-skip test exercises
    the previously unreachable caller() failure branch.

v1.1.5

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 08:11

Documentation only — no code change (external review round 6).

  • Docs External review round 6: problem.Write notes that invalid UTF-8
    in public strings is replaced with U+FFFD by encoding/json itself (the HTTP
    boundary tolerates bytes the gRPC transport refuses); DESIGN.md §9 carries
    the poisoned-message caveat recorded in grpcerr's entry below.

v1.1.4

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 04:06

A display fix plus review-round docs and tests (Gemini finding 3; external
review round 5).

  • Fixed Frame.String() no longer renders a bogus " (:0)" location for
    unresolved frames (both File and Line zero — e.g. the "unknown"
    sentinel a zero-value Error resolves to). A frame with either part set
    still prints the location.
  • Docs External-review (round 5) drift swept: DESIGN.md §9's RetryDelay
    spec now carries the v1.1.2 CheckValid gate (delays outside the protobuf
    Duration range are rejected, not saturated); the §6 verb table gains the
    unknown-verb fallback row; problem.Write documents that a panicking
    MarshalJSON on a public field value propagates (matching encoding/json).
  • Tests Previously uncovered branches pinned: %+v on a nil *Error
    ("<nil>"), the unknown-verb %v fallback, and Wrapf's non-nil path.

otelerr/v1.0.1

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 13:01

External review round 7 finding.

  • Fixed RecordSpan no longer lets a With attr sharing the reserved
    "errtrail.code" key duplicate the taxonomy attribute on the exported
    exception event. A duplicate key's resolution (first-wins, last-wins, or
    an array) varies by tracing backend — an accidental or hostile attr named
    errtrail.code could silently corrupt the one attribute alerts and
    dashboards key off of. The attr is now dropped; the real code always
    wins. No core requirement change.

grpcerr/v1.3.0

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 08:56

Dynamic retry pushback (ROADMAP §5). Requires core v1.3.0.

  • Changed The RetryInfo detail now prefers the error's own delay
    (errtrail.WithRetryDelay — dynamic pushback) over the code's registered
    static RetryAfter delay; errors without a per-error delay keep the
    registry behavior exactly. A delay below a WithoutPublic barrier is not
    exposed, while the registry delay — code configuration, not error data —
    still applies. Pinned end-to-end over bufconn: the dynamic value reaches
    the client's RetryDelay reader. Requires core ≥ v1.3.0.

grpcerr/v1.2.0

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 08:29

Frame-placement improvement (ROADMAP §4). Requires core v1.2.0.

  • Changed FromError / FromStatus record the frame at their caller
    (via core's new WrapSkip(1)), so traces start where the wire error
    entered your code. The documented workaround — "the frame points inside
    grpcerr; wrap the result at the call site" — is retired; an existing
    errtrail.Wrap at the call site keeps working and simply adds one more
    frame. Requires core ≥ v1.2.0.

grpcerr/v1.1.5

Choose a tag to compare

@repenguin22 repenguin22 released this 11 Jul 08:11

Documentation and tests only — no code change (external review round 6).

  • Docs External review round 6 (measured over bufconn): an invalid-UTF-8
    public message sits outside v1.1.4's per-detail isolation — it lives on
    the Status proto itself, so the transport cannot marshal the
    grpc-status-details-bin trailer and the client receives code+message but
    zero details (custom-code recovery degrades to the numeric wire code;
    only grpclog sees the failure). ToStatus documents the hazard and the
    utf8.ValidString guidance; FromError warns that the wire message can
    carry raw non-UTF-8 bytes, so echoing it unvalidated into WithPublic
    poisons your own response's details. Guarding it in code was considered
    and rejected (ROADMAP): the trailer is the transport's to build, and
    rewriting the message would be silent mutation.
  • Tests A standing test pins both halves of the mechanism: the
    in-process attach succeeds (3 details) while proto.Marshal(st.Proto())
    fails on the poisoned message — so a grpc-go/protobuf change altering
    either half is noticed.