$npx -y skills add samber/cc-skills-golang --skill golang-samber-oopsStructured error handling in Golang with samber/oops — error builders, stack traces, error codes, error context, error wrapping, error attributes, user-facing vs developer messages, panic recovery, and logger integration. Apply when using or adopting samber/oops, or when the code
| 1 | **Persona:** You are a Go engineer who treats errors as structured data. Every error carries enough context — domain, attributes, trace — for an on-call engineer to diagnose the problem without asking the developer. |
| 2 | |
| 3 | # samber/oops Structured Error Handling |
| 4 | |
| 5 | **samber/oops** is a drop-in replacement for Go's standard error handling that adds structured context, stack traces, error codes, public messages, and panic recovery. Variable data goes in `.With()` attributes (not the message string), so APM tools (Datadog, Loki, Sentry) can group errors properly. Unlike the stdlib approach (adding `slog` attributes at the log site), oops attributes travel with the error through the call stack. |
| 6 | |
| 7 | ## Why use samber/oops |
| 8 | |
| 9 | Standard Go errors lack context — you see `connection failed` but not which user triggered it, what query was running, or the full call stack. `samber/oops` provides: |
| 10 | |
| 11 | - **Structured context** — key-value attributes on any error |
| 12 | - **Stack traces** — automatic call stack capture |
| 13 | - **Error codes** — machine-readable identifiers |
| 14 | - **Public messages** — user-safe messages separate from technical details |
| 15 | - **Low-cardinality messages** — variable data in `.With()` attributes, not the message string, so APM tools group errors properly |
| 16 | |
| 17 | This skill is not exhaustive. Please refer to library documentation and code examples for more information. For Go package docs, symbols, versions, importers, and known vulnerabilities, → See `samber/cc-skills-golang@golang-pkg-go-dev` skill (`godig`) — prefer it over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See `samber/cc-skills-golang@golang-gopls` skill (`gopls`). Context7 remains a fallback for docs not indexed on pkg.go.dev. |
| 18 | |
| 19 | ## Core pattern: Error builder chain |
| 20 | |
| 21 | All `oops` errors use a fluent builder pattern: |
| 22 | |
| 23 | ```go |
| 24 | err := oops. |
| 25 | In("user-service"). // domain/feature |
| 26 | Tags("database", "postgres"). // categorization |
| 27 | Code("network_failure"). // machine-readable identifier |
| 28 | User("user-123", "email", "foo@bar.com"). // user context |
| 29 | With("query", query). // custom attributes |
| 30 | Errorf("failed to fetch user: %s", "timeout") |
| 31 | ``` |
| 32 | |
| 33 | Terminal methods: |
| 34 | |
| 35 | - `.Errorf(format, args...)` — create a new error |
| 36 | - `.Wrap(err)` — wrap an existing error |
| 37 | - `.Wrapf(err, format, args...)` — wrap with a message |
| 38 | - `.Join(err1, err2, ...)` — combine multiple errors |
| 39 | - `.Recover(fn)` / `.Recoverf(fn, format, args...)` — convert panic to error |
| 40 | |
| 41 | ### Error builder methods |
| 42 | |
| 43 | | Methods | Use case | |
| 44 | | --- | --- | |
| 45 | | `.With("key", value)` | Add custom key-value attribute (lazy `func() any` values supported) | |
| 46 | | `.WithContext(ctx, "key1", "key2")` | Extract values from Go context into attributes (lazy values supported) | |
| 47 | | `.In("domain")` | Set the feature/service/domain | |
| 48 | | `.Tags("auth", "sql")` | Add categorization tags (query with `err.HasTag("tag")`) | |
| 49 | | `.Code("iam_authz_missing_permission")` | Set machine-readable error identifier/slug | |
| 50 | | `.Public("Could not fetch user.")` | Set user-safe message (separate from technical details) | |
| 51 | | `.Hint("Runbook: https://doc.acme.org/doc/abcd.md")` | Add debugging hint for developers | |
| 52 | | `.Owner("team/slack")` | Identify responsible team/owner | |
| 53 | | `.User(id, "k", "v")` | Add user identifier and attributes | |
| 54 | | `.Tenant(id, "k", "v")` | Add tenant/organization context and attributes | |
| 55 | | `.Trace(id)` | Add trace / correlation ID (default: ULID) | |
| 56 | | `.Span(id)` | Add span ID representing a unit of work/operation (default: ULID) | |
| 57 | | `.Time(t)` | Override error timestamp (default: `time.Now()`) | |
| 58 | | `.Since(t)` | Set duration based on time since `t` (exposed via `err.Duration()`) | |
| 59 | | `.Duration(d)` | Set explicit error duration | |
| 60 | | `.Request(req, includeBody)` | Attach `*http.Request` (optionally including body) | |
| 61 | | `.Response(res, includeBody)` | Attach `*http.Response` (optionally including body) | |
| 62 | | `oops.FromContext(ctx)` | Start from an `OopsErrorBuilder` stored in a Go context | |
| 63 | |
| 64 | ## Common scenarios |
| 65 | |
| 66 | ### Database/repository lay |