{"id":36687789,"url":"https://github.com/goark/errs","last_synced_at":"2026-05-09T10:30:09.595Z","repository":{"id":47603890,"uuid":"199092363","full_name":"goark/errs","owner":"goark","description":"Error handling for Golang","archived":false,"fork":false,"pushed_at":"2023-06-26T11:33:33.000Z","size":101,"stargazers_count":34,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-13T09:57:20.284Z","etag":null,"topics":["error-handling","go","golang","golang-package"],"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/goark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-07-26T23:38:10.000Z","updated_at":"2024-07-04T14:27:53.000Z","dependencies_parsed_at":"2023-07-13T15:31:11.719Z","dependency_job_id":null,"html_url":"https://github.com/goark/errs","commit_stats":{"total_commits":48,"total_committers":1,"mean_commits":48.0,"dds":0.0,"last_synced_commit":"dce4948f9d97afc29748992bbbe7ac9fca50208a"},"previous_names":["spiegel-im-spiegel/errs"],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/goark/errs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goark%2Ferrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goark%2Ferrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goark%2Ferrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goark%2Ferrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/goark","download_url":"https://codeload.github.com/goark/errs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goark%2Ferrs/sbom","scorecard":{"id":432790,"data":{"date":"2025-08-11","repo":{"name":"github.com/goark/errs","commit":"9832c926e71456907d07fa1158d573ea41d19f66"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/16 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:58: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/lint.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/lint.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/vulns.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/vulns.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/vulns.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/vulns.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/vulns.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/goark/errs/vulns.yml/master?enable=pin","Warn: goCommand not pinned by hash: .github/workflows/lint.yml:51","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   0 out of   1 goCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql-analysis.yml:27","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:28","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/lint.yml:9","Warn: no topLevel permission defined: .github/workflows/vulns.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 27 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T03:45:59.915Z","repository_id":47603890,"created_at":"2025-08-19T03:45:59.915Z","updated_at":"2025-08-19T03:45:59.915Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T10:58:46.209Z","status":"ssl_error","status_checked_at":"2026-01-12T10:58:42.742Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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","go","golang","golang-package"],"created_at":"2026-01-12T11:16:20.807Z","updated_at":"2026-05-09T10:30:09.538Z","avatar_url":"https://github.com/goark.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [errs] -- Error handling for Golang\n\n[![ci status](https://github.com/goark/errs/workflows/ci/badge.svg)](https://github.com/goark/errs/actions)\n[![codeql status](https://github.com/goark/errs/workflows/CodeQL/badge.svg)](https://github.com/goark/errs/actions)\n[![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/goark/errs/master/LICENSE)\n[![GitHub release](http://img.shields.io/github/release/goark/errs.svg)](https://github.com/goark/errs/releases/latest)\n\nPackage [errs] implements functions to manipulate error instances.\nThis package is required Go 1.20 or later.\n\n**Migrated repository to [github.com/goark/errs][errs]**\n\n## Design goals\n\n- Wrap any `error` and collect context at the point of failure\n- Add arbitrary key/value context with `WithContext`\n- Include caller function name in context by default\n- Print structured error data with `%+v` (JSON-like output)\n- Handle multi-errors in a concurrency-safe way via `errs.Errors`\n\n## Development\n\n### Requirements\n\n- Go 1.20 or later\n- [Task](https://taskfile.dev/) command\n\n### Local validation\n\n```text\ntask test\ntask govulncheck\n```\n\nRun all maintenance tasks:\n\n```text\ntask\n```\n\n## CI Workflows\n\n- `ci`: lint (`golangci-lint` with `gosec`), tests, and `govulncheck`\n- `CodeQL`: scheduled and push/PR static analysis\n\n## Usage\n\n### Sample programs\n\nAll sample files under `sample/` use the `run` build tag.\n\n```text\ngo run -tags run ./sample/sample1.go\n```\n\n- New error with cause and context: [sample/sample1.go](sample/sample1.go)\n- Wrap existing error with context: [sample/sample2.go](sample/sample2.go)\n- Wrap sentinel error with cause: [sample/sample3.go](sample/sample3.go)\n- Multiple causes with `errors.Join`: [sample/sample4.go](sample/sample4.go)\n- Multi-error with `errs.Join`: [sample/sample5.go](sample/sample5.go)\n- Concurrency-safe multi-error accumulation: [sample/sample6.go](sample/sample6.go)\n- Zap structured logging object example: [zapobject/example_test.go](zapobject/example_test.go)\n\n### Print formats\n\n- `%v`: human readable error message\n- `%#v`: Go-syntax-like internal structure\n- `%+v`: structured JSON-like representation\n\n### Helper functions compatible with stdlib\n\n`errs.Is`, `errs.As`, and `errs.Unwrap` are thin wrappers around `errors.Is`, `errors.As`, and `errors.Unwrap`.\n\n`errs.Unwraps` returns `[]error` and works for both single-cause and multi-cause errors.\nFor a single cause, it returns a one-element slice.\nFor multiple causes, it returns all causes as a slice.\n\n`errs.EncodeJSON` serializes generic `error` values by traversing unwrap chains when possible.\n\n### Edge-case behavior\n\n- `errs.New(\"\")` returns `nil`\n- `errs.Wrap(nil)` returns `nil`\n- If `WithCause` is given multiple times, the last cause is used\n- `errs.Join(...)` ignores `nil` arguments and returns `nil` if all arguments are `nil`\n\n### Concurrency notes\n\n- `errs.Errors` is goroutine-safe for container operations such as `Add`, `ErrorOrNil`, and `Unwrap`.\n- Errors stored in `errs.Errors` are not guaranteed to be goroutine-safe.\n- `errs.Error` has mutable state (`Context` map), so avoid concurrent mutation while formatting or encoding the same instance.\n\n### Create new error instance with cause\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n\n    \"github.com/goark/errs\"\n)\n\nfunc checkFileOpen(path string) error {\n    file, err := os.Open(path)\n    if err != nil {\n        return errs.New(\n            \"file open error\",\n            errs.WithCause(err),\n            errs.WithContext(\"path\", path),\n        )\n    }\n    defer file.Close()\n\n    return nil\n}\n\nfunc main() {\n    if err := checkFileOpen(\"not-exist.txt\"); err != nil {\n        fmt.Printf(\"%v\\n\", err)  // file open error: open not-exist.txt: no such file or directory\n        fmt.Printf(\"%#v\\n\", err) // *errs.Error{Err:\u0026errors.errorString{s:\"file open error\"}, Cause:\u0026fs.PathError{Op:\"open\", Path:\"not-exist.txt\", Err:0x2}, Context:map[string]interface {}{\"function\":\"main.checkFileOpen\", \"path\":\"not-exist.txt\"}}\n        fmt.Printf(\"%+v\\n\", err) // {\"Type\":\"*errs.Error\",\"Err\":{\"Type\":\"*errors.errorString\",\"Msg\":\"file open error\"},\"Context\":{\"function\":\"main.checkFileOpen\",\"path\":\"not-exist.txt\"},\"Cause\":{\"Type\":\"*fs.PathError\",\"Msg\":\"open not-exist.txt: no such file or directory\",\"Cause\":{\"Type\":\"syscall.Errno\",\"Msg\":\"no such file or directory\"}}}\n    }\n}\n```\n\n### Wrapping error instance\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n\n    \"github.com/goark/errs\"\n)\n\nfunc checkFileOpen(path string) error {\n    file, err := os.Open(path)\n    if err != nil {\n        return errs.Wrap(\n            err,\n            errs.WithContext(\"path\", path),\n        )\n    }\n    defer file.Close()\n\n    return nil\n}\n\nfunc main() {\n    if err := checkFileOpen(\"not-exist.txt\"); err != nil {\n        fmt.Printf(\"%v\\n\", err)  // open not-exist.txt: no such file or directory\n        fmt.Printf(\"%#v\\n\", err) // *errs.Error{Err:\u0026fs.PathError{Op:\"open\", Path:\"not-exist.txt\", Err:0x2}, Cause:\u003cnil\u003e, Context:map[string]interface {}{\"function\":\"main.checkFileOpen\", \"path\":\"not-exist.txt\"}}\n        fmt.Printf(\"%+v\\n\", err) // {\"Type\":\"*errs.Error\",\"Err\":{\"Type\":\"*fs.PathError\",\"Msg\":\"open not-exist.txt: no such file or directory\",\"Cause\":{\"Type\":\"syscall.Errno\",\"Msg\":\"no such file or directory\"}},\"Context\":{\"function\":\"main.checkFileOpen\",\"path\":\"not-exist.txt\"}}\n    }\n}\n```\n\n### Wrapping error instance with cause\n\n```go\npackage main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"os\"\n\n    \"github.com/goark/errs\"\n)\n\nfunc checkFileOpen(path string) error {\n    file, err := os.Open(path)\n    if err != nil {\n        return errs.Wrap(\n            errors.New(\"file open error\"),\n            errs.WithCause(err),\n            errs.WithContext(\"path\", path),\n        )\n    }\n    defer file.Close()\n\n    return nil\n}\n\nfunc main() {\n    if err := checkFileOpen(\"not-exist.txt\"); err != nil {\n        fmt.Printf(\"%v\\n\", err)  // file open error: open not-exist.txt: no such file or directory\n        fmt.Printf(\"%#v\\n\", err) // *errs.Error{Err:\u0026errors.errorString{s:\"file open error\"}, Cause:\u0026fs.PathError{Op:\"open\", Path:\"not-exist.txt\", Err:0x2}, Context:map[string]interface {}{\"function\":\"main.checkFileOpen\", \"path\":\"not-exist.txt\"}}\n        fmt.Printf(\"%+v\\n\", err) // {\"Type\":\"*errs.Error\",\"Err\":{\"Type\":\"*errors.errorString\",\"Msg\":\"file open error\"},\"Context\":{\"function\":\"main.checkFileOpen\",\"path\":\"not-exist.txt\"},\"Cause\":{\"Type\":\"*fs.PathError\",\"Msg\":\"open not-exist.txt: no such file or directory\",\"Cause\":{\"Type\":\"syscall.Errno\",\"Msg\":\"no such file or directory\"}}}\n    }\n}\n```\n\n### Create new error instance with multiple causes\n\n```go\npackage main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"io\"\n    \"os\"\n\n    \"github.com/goark/errs\"\n)\n\nfunc generateMultiError() error {\n    return errs.New(\"error with multiple causes\", errs.WithCause(errors.Join(os.ErrInvalid, io.EOF)))\n}\n\nfunc main() {\n    err := generateMultiError()\n    fmt.Printf(\"%+v\\n\", err)            // {\"Type\":\"*errs.Error\",\"Err\":{\"Type\":\"*errors.errorString\",\"Msg\":\"error with multiple causes\"},\"Context\":{\"function\":\"main.generateMultiError\"},\"Cause\":{\"Type\":\"*errors.joinError\",\"Msg\":\"invalid argument\\nEOF\",\"Cause\":[{\"Type\":\"*errors.errorString\",\"Msg\":\"invalid argument\"},{\"Type\":\"*errors.errorString\",\"Msg\":\"EOF\"}]}}\n    fmt.Println(errors.Is(err, io.EOF)) // true\n}\n```\n\n### Handling multiple errors\n\n```go\npackage main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"io\"\n    \"os\"\n\n    \"github.com/goark/errs\"\n)\n\nfunc generateMultiError() error {\n    return errs.Join(os.ErrInvalid, io.EOF)\n}\n\nfunc main() {\n    err := generateMultiError()\n    fmt.Printf(\"%+v\\n\", err)            // {\"Type\":\"*errs.Errors\",\"Errs\":[{\"Type\":\"*errors.errorString\",\"Msg\":\"invalid argument\"},{\"Type\":\"*errors.errorString\",\"Msg\":\"EOF\"}]}\n    fmt.Println(errors.Is(err, io.EOF)) // true\n}\n```\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"sync\"\n\n    \"github.com/goark/errs\"\n)\n\nfunc generateMultiError() error {\n    errlist := \u0026errs.Errors{}\n    var wg sync.WaitGroup\n    for i := 1; i \u003c= 2; i++ {\n        i := i\n        wg.Add(1)\n        go func() {\n            defer wg.Done()\n            errlist.Add(fmt.Errorf(\"error %d\", i))\n        }()\n    }\n    wg.Wait()\n    return errlist.ErrorOrNil()\n}\n\nfunc main() {\n    err := generateMultiError()\n    fmt.Printf(\"%+v\\n\", err) // {\"Type\":\"*errs.Errors\",\"Errs\":[{\"Type\":\"*errors.errorString\",\"Msg\":\"error 2\"},{\"Type\":\"*errors.errorString\",\"Msg\":\"error 1\"}]}\n}\n```\n\n### Structured logging with Zap\n\nUse the submodule `github.com/goark/errs/zapobject` to log errors as structured objects.\n\n```go\nlogger.Error(\"failed\", zap.Object(\"error\", zapobject.New(err)))\n```\n\nWithout `zapobject`, `zap.Error(err)` writes string fields only.\n\n## Background article (Japanese)\n\n- [Go 言語用エラーハンドリング・パッケージ](https://text.baldanders.info/release/errs-package-for-golang/)\n\n[errs]: https://github.com/goark/errs \"goark/errs: Error handling for Golang\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoark%2Ferrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoark%2Ferrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoark%2Ferrs/lists"}