{"id":49441983,"url":"https://github.com/scality/go-errors","last_synced_at":"2026-04-29T20:37:33.336Z","repository":{"id":332382401,"uuid":"1082430233","full_name":"scality/go-errors","owner":"scality","description":"A standard library to deal with errors","archived":false,"fork":false,"pushed_at":"2026-04-29T04:08:37.000Z","size":63,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T06:15:39.653Z","etag":null,"topics":["error-handling","errors","golang","golang-library","library","scality"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scality.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-24T08:24:15.000Z","updated_at":"2026-04-24T07:38:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/scality/go-errors","commit_stats":null,"previous_names":["scality/go-errors"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/scality/go-errors","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fgo-errors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fgo-errors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fgo-errors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fgo-errors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scality","download_url":"https://codeload.github.com/scality/go-errors/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fgo-errors/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32443568,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T20:22:27.477Z","status":"ssl_error","status_checked_at":"2026-04-29T20:22:26.507Z","response_time":110,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["error-handling","errors","golang","golang-library","library","scality"],"created_at":"2026-04-29T20:37:31.977Z","updated_at":"2026-04-29T20:37:33.330Z","avatar_url":"https://github.com/scality.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-errors\n\nA Go library for enhanced error handling with stack traces, structured error information, and error wrapping capabilities.\n\n## Features\n\n- **Stack Traces**: Automatic capture of function location, file and line number\n- **Error Wrapping**: Chain errors with causes for better error context\n- **Structured Errors**: Add identifiers, details, and arbitrary properties via options\n- **Single Entry Point**: `Wrap(error, ...Option)` works with both standard errors and go-errors\n- **Standard Library Compatible**: Implements standard error interface and works with `errors.Is()`, `errors.As()`, and `errors.Unwrap()`\n- **JSON Serialization**: Built-in JSON marshaling for logging and debugging\n\n## Installation\n\n```bash\ngo get github.com/scality/go-errors\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/scality/go-errors\"\n)\n\nfunc main() {\n\tvar ErrDB = errors.New(\"database error\")\n\terr := errors.Wrap(ErrDB,\n\t\terrors.WithIdentifier(1001),\n\t\terrors.WithDetail(\"connection timeout\"),\n\t\terrors.WithProperty(\"host\", \"localhost\"),\n\t)\n\tfmt.Println(err)\n}\n```\n\n## Usage Examples\n\n### Adding Details and Properties\n\n```go\n// Define domain errors with New()\nvar (\n\tErrValidationFailed = errors.New(\"validation failed\")\n\tErrRequestFailed    = errors.New(\"request failed\")\n\tErrDatabaseError    = errors.New(\"database error\")\n)\n\n// Multiple details: each WithDetail() / WithDetailf() appends to the details slice\nerr1 := errors.Wrap(ErrValidationFailed,\n\terrors.WithDetail(\"email is required\"),\n\terrors.WithDetail(\"password must be at least 8 characters\"),\n)\n\n// Formatted details\nerr2 := errors.Wrap(ErrRequestFailed,\n\terrors.WithDetailf(\"failed to connect to %s:%d\", \"api.example.com\", 443),\n\terrors.WithDetail(\"timeout after 30 seconds\"),\n)\n\n// Properties (key-value pairs)\nerr3 := errors.Wrap(ErrRequestFailed,\n\terrors.WithProperty(\"url\", \"https://api.example.com\"),\n\terrors.WithProperty(\"status_code\", 500),\n)\n\n// Multiple properties (one option per property)\nerr4 := errors.Wrap(ErrDatabaseError,\n\terrors.WithProperty(\"host\", \"localhost\"),\n\terrors.WithProperty(\"port\", 5432),\n\terrors.WithProperty(\"database\", \"myapp\"),\n)\n```\n\n### CausedBy\n\n```go\nvar ErrUserNotFound = errors.New(\"user not found\")\n\nfunc getUserByID(id string) (*User, error) {\n\tuser, err := db.Query(id)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(ErrUserNotFound,\n\t\t\terrors.WithIdentifier(404000),\n\t\t\terrors.CausedBy(err),\n\t\t)\n\t}\n\treturn user, nil\n}\n\n// Convenient wrapping with Wrap() - adds message and stack trace\nfunc getUser(id int) (*User, error) {\n\tuser, err := db.Query(id)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err,\n\t\t\terrors.WithDetail(\"failed to fetch user from database\"),\n\t\t)\n\t}\n\treturn user, nil\n}\n```\n\n### Identifier Concatenation\n\nEach call to `Wrap` can add an identifier segment; segments are concatenated with `-` to trace the error path through the call stack (e.g. `19-12-2`).\n\n```go\nvar ErrForbidden = errors.New(\"forbidden\")\n\nfunc call1() error {\n\treturn errors.Wrap(call2(), errors.WithIdentifier(19))\n}\n\nfunc call2() error {\n\treturn errors.Wrap(call3(),\n\t\terrors.WithDetail(\"missing required role\"),\n\t\terrors.WithProperty(\"Role\", \"Reader\"),\n\t\terrors.WithIdentifier(12),\n\t)\n}\n\nfunc call3() error {\n\t_, err := os.Open(\"test.txt\")\n\treturn errors.Wrap(ErrForbidden,\n\t\terrors.WithIdentifier(2),\n\t\terrors.WithDetail(\"permission denied\"),\n\t\terrors.WithProperty(\"File\", \"test.txt\"),\n\t\terrors.CausedBy(err),\n\t)\n}\n```\n\n### Working with Standard Library\n\n```go\n// Using errors.Is for comparison (compares Title and Identifier)\nif errors.Is(err, notFoundErr) {\n\t// Handle not found error\n}\n\n// Using errors.As to access structured fields\nvar e *errors.Error\nif errors.As(err, \u0026e) {\n\tfmt.Printf(\"Title: %s\\n\", e.Title)\n\tfmt.Printf(\"Identifier: %v\\n\", e.Identifier)   // []uint32\n\tfmt.Printf(\"Details: %v\\n\", e.Details)         // []string\n\tfmt.Printf(\"Properties: %v\\n\", e.Properties)\n\n\tfor i, detail := range e.Details {\n\t\tfmt.Printf(\"  Detail %d: %s\\n\", i, detail)\n\t}\n}\n```\n\n### Converting Standard Errors\n\n```go\n// Wrap any standard error to add stack trace, details, and properties\nstdErr := fmt.Errorf(\"something went wrong\")\nerr := errors.Wrap(stdErr,\n\terrors.WithDetail(\"additional context\"),\n\terrors.WithProperty(\"source\", \"legacy\"),\n)\n```\n\n### Specific use-case with Is()\n\nIs() compares this error with another error for equality. Two errors match if they have same Title and same Identifier*\n(*) or if one is a parent of the other.\n\nFor example:\nIf e1.Identifier: \"2-1\" and e2.Identifier: \"3-2-1\", then\n```go\ne2.Is(e1) return True // e1 is a parent of e2\ne1.Is(e2) return False\n```\n\n## Output Format\n\nThe `Error()` method produces output in the following format:\n\n```\ntitle (id): detail1: detail2: detail3: key1='value1', key2='value2', at=[(func='func1Name', file='file.go', line='21'), (func='func2Name', file='file.go', line='10')], caused by: underlying error\n```\n\nDetails are stored as a slice and joined with `: ` when the error is formatted. Each `WithDetail()` or `WithDetailf()` option appends to this slice.\n\nExample with multiple details:\n\n```\ndatabase error (1001): connection timeout: retry limit exceeded: host='localhost', port='5432', at=[(func='connectDB', file='db.go', line='42')], caused by: dial tcp: connection refused\n```\n\nExample with wrapped error:\n\n```\nunknown error (0): failed to fetch user from database: at=[(func='getUser', file='user.go', line='25')], caused by: connection refused\n```\n\n## JSON formatting message\n| Marker | Description                |\n| ------ | -------------------------- |\n| `%v`   | JSON (without stack)       |\n| `%+v`  | Extended JSON (with stack) |\n\nExample with JSON without stack (%v):\n\n```\n{\"title\":\"forbidden\",\"identifier\":[2,12,19],\"details\":[\"permission denied\",\"missing required role\"],\"properties\":{\"File\":\"test.txt\",\"Role\":\"Reader\"},\"cause\":\"open test.txt: permission denied\"}\n```\n\nExample with extended JSON, with stack (%+v):\n\n```\n{\"title\":\"forbidden\",\"identifier\":[2,12,19],\"details\":[\"permission denied\",\"missing required role\"],\"properties\":{\"File\":\"test.txt\",\"Role\":\"Reader\"},\"cause\":\"open test.txt: permission denied\",\"stack\":[{\"function\":\"main.call1\",\"file\":\"/path/to/main.go\",\"line\":25},{\"function\":\"main.call2\",\"file\":\"/path/to/main.go\",\"line\":29},{\"function\":\"main.call3\",\"file\":\"/path/to/main.go\",\"line\":38}]}\n```\n\n## Best Practices\n\n1. **Use `Wrap(err, ...options)`** as the single entry point for both standard errors and go-errors; it captures stack traces and applies options.\n2. **Use identifiers** for errors that need programmatic handling (e.g., HTTP status codes); they are concatenated across the call stack when re-wrapping.\n3. **Add details** with `WithDetail()` or `WithDetailf()`; they are stored as a slice and displayed in order.\n4. **Add properties** with `WithProperty(key, value)` for debugging context (IDs, URLs, parameters, etc.).\n5. **Use `CausedBy(err)`** when wrapping to record the underlying cause and maintain error chains for `errors.Is` / `errors.Unwrap`.\n6. **Use `New(title)`** to define sentinel errors; pass them to `Wrap` and add options at each layer.\n\n### Details vs Properties\n\n- **Details** (slice of strings): Human-readable context that appears in error messages, ordered and concatenated with `: `.\n- **Properties** (key-value map): Structured data for debugging/logging, useful for searching and filtering logs.\n\n## License\n\nSee [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscality%2Fgo-errors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscality%2Fgo-errors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscality%2Fgo-errors/lists"}