{"id":32948818,"url":"https://github.com/quantumcycle/metaerr","last_synced_at":"2026-01-14T16:25:45.370Z","repository":{"id":197459459,"uuid":"698678397","full_name":"quantumcycle/metaerr","owner":"quantumcycle","description":"Errors with metadata for Golang","archived":false,"fork":false,"pushed_at":"2025-05-09T18:58:40.000Z","size":32,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-09T19:49:00.533Z","etag":null,"topics":["errors","golang","golang-library"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quantumcycle.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-09-30T16:21:18.000Z","updated_at":"2025-05-09T18:57:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"9aac6c6e-8105-4c2c-9585-94322e27a5ef","html_url":"https://github.com/quantumcycle/metaerr","commit_stats":null,"previous_names":["quantumcycle/metaerr"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/quantumcycle/metaerr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quantumcycle%2Fmetaerr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quantumcycle%2Fmetaerr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quantumcycle%2Fmetaerr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quantumcycle%2Fmetaerr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quantumcycle","download_url":"https://codeload.github.com/quantumcycle/metaerr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quantumcycle%2Fmetaerr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28425805,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T15:24:48.085Z","status":"ssl_error","status_checked_at":"2026-01-14T15:23:41.940Z","response_time":107,"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":["errors","golang","golang-library"],"created_at":"2025-11-12T20:00:35.663Z","updated_at":"2026-01-14T16:25:45.362Z","avatar_url":"https://github.com/quantumcycle.png","language":"Go","readme":"# MetaErr\n\n[![GoDoc](https://pkg.go.dev/badge/github.com/quantumcycle/metaerr)](https://pkg.go.dev/github.com/quantumcycle/metaerr?tab=doc)\n[![Go Report Card](https://goreportcard.com/badge/github.com/quantumcycle/metaerr)](https://goreportcard.com/report/github.com/quantumcycle/metaerr)\n[![codecov](https://codecov.io/gh/quantumcycle/metaerr/graph/badge.svg?token=3EFILQUGE9)](https://codecov.io/gh/quantumcycle/metaerr)\n\nMetaerr is a golang package to create or wrap errors with custom metadata and location.\n\nThis library requires Golang 1.21+.\n\n## Why\n\nI used github.com/pkg/errors before, and the stack traces were extensive (like Java) and not very useful. Then, I came across the [Fault library](https://github.com/Southclaws/fault), which was amazing, but the way I wanted to use it clashed with some of the opinions embedded in the library.\nThere is also [samber oops library](https://github.com/samber/oops), but the problem was that it's not extendable to have custom metadata.\n\nThis is why I decided to create this simple library. It utilizes the same \"stack trace\" model as Fault, in the sense that you will see the stack pertaining to the locations of error creation, but also adds regular stacktraces on top of that as an option.\n\nThe next feature it offers is the ability to add any number of key-value metadata entries to each error, including wrapped errors. This is useful if you want to attach metadata at the time of error creation and then leverage that metadata during resolution. A common use case is having a generic HTTP error handler for an API that can use the metadata to determine the HTTP status or construct an error payload to send to the user. Another use case would be logging and alerting. If you convert the metadata into fields in a JSON logger, you could have different alerting rules for logged ERRORS based on the metadata; for example, errors with the metadata tag containing \"security\" could trigger an immediate alert.\n\n## Install\n\n```\ngo get -u github.com/quantumcycle/metaerr\n```\n\n## Usage\n\nMetaerr can be used with the Go standard errors package, and they are also compatible with error wrapping introduced in Go 1.13.\n\nThere is 2 ways to use the library. Using the errors directly, or using the builder. **The builder approach is recommended.**\n\n### using the errors directly\n\nTo create an new MetaErr from a string, use\n\n```golang\nerr := metaerr.New(\"failure\")\n```\n\nTo create a new MetaErr by wrapping an existing error, use\n\n```golang\nerr := metaerr.Wrap(err, \"failure\")\n```\n\nThe if you want to add metadata, you first create the metadata, and pass it as an option.\n\n```golang\n//Create an metadata called ErrorCode\nvar ErrorCode = metaerr.StringMeta(\"error_code\")\n\nfunc main() {\n\trootCause := metaerr.New(\"failure\", metaerr.WithMeta(ErrorCode(\"x01\"))\n\terr := metaerr.Wrap(rootCause, \"cannot fetch content\")\n\tfmt.Printf(\"%+v\", err)\n}\n```\nwill print\n```\ncannot fetch content\n        at .../quantumcycle/metaerr/cmd/main.go:12\nfailure [error_code=x01]\n        at .../quantumcycle/metaerr/cmd/main.go:11\n\n```\n\n### using the builder\n\nUsing the errors directly is ok, but it's a bit verbose just to create errors. Using the builder is a better approach \nand reduce boilerplate.\n\nTo use the builder, just create an instance of the builder with the relevant options for you, and then use it.\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/quantumcycle/metaerr\"\n)\n\nvar errors = metaerr.NewBuilder(metaerr.WithStackTrace(0, 2))\nvar ErrorCode = metaerr.StringMeta(\"error_code\")\n\nfunc main() {\n\terr := errors.Meta(ErrorCode(\"test\")).Newf(\"failure with user %s\", \"test\")\n\tfmt.Printf(\"%+v\\n\", err)\n}\n```\n\n### creating your own customized builder\n\nThe builder this library provides can be use as a standalone builder, but you should consider creating your own builder\nby decorating the provided builder. The reason is that it's still very verbose to pass each instance of the metadata\nwhen creating an error.\n\nLook at [this file](./example/errors/builder.go) for an example of a builder with 2 possible metadata (errorCode and \ntags), and [this file](./example/main.go) as an example of using this builder.\n\nThe builder is immutable/thread safe, so you can have a base builder and then call `.Context(ctx)` on it without impacting the\nrest of your code using the same builder. It does share the same metadata slice though, but there is no way to modify\nthe slice after creation, so it's safe.\n\n### Getting the err message, location, and metadata\n\nIn the example above, we use the Printf formatting to display the error, metadata and location all in one gulp. \nYou can however use the provided helper function to get the individual parts\n\n```golang\nerr := metaerr.New(\"failure\")\nerr.Error() //returns failure\nmerr := metaerr.AsMetaErr(err)\nmerr.Location() //returns .../mysource/mypackage/file.go:22\n\n// will print error_code:x01\nmeta := metaerr.GetMeta(err, false)\nfor k, values := range meta {\n  for _, val := range values {\n    fmt.Println(k + \":\" + val)\n  }\n}\n\n```\n\n### Options\n\nYou can provide options to modify the errors during creation. \n\n#### WithMeta\n\nThis is the main option you would be using. It allows you to add metadata to the error. You can add as many metadata as you want.\nThe library propose 4 built-in metadata builders:\n- StringMeta: to add a string metadata\n- StringsMeta: to add a slice of string metadata\n- StringerMeta: to add any type that implements the Stringer interface as metadata\n- StringMetaFromContext: to add a string metadata from a context (see `WithContext` below)\n\n#### WithLocationSkip\nBy default, when creating an error, Metaerr will skip all stack frames related to metaerr to determine the error's creation location. \nThis works well when you call Metaerr directly at the place where the error is created in your codebase. However, there is a use case \nwhere you use a factory, or your own builder to create errors. In this case, if you use the standard `metaerr.New` function, the reported \nlocation will be the line where metaerr is called to create the error, which may be within your error factory or builder function. \nYou probably don't want to have all your locations pointing to the same line. To address this, you can use the `metaerr.WithLocationSkip` \noption to add additional call stack skips to determine the location. Here is an example:\n\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/quantumcycle/metaerr\"\n)\n\nvar Tag = metaerr.StringMeta(\"tag\")\n\nfunc CreateDatabaseError(reason string) error {\n\treturn metaerr.New(reason, metaerr.WithLocationSkip(1), metaerr.WithMeta(Tag(\"database\")))\n}\n\nfunc main() {\n\tdbErr := CreateDatabaseError(\"no such table [User]\")\n\tfmt.Printf(\"%+v\", dbErr)\n}\n\n```\n\nwhich will output\n\n```\nno such table [User] [tag=database]\n        at .../github.com/quantumcycle/metaerr/cmd/main.go:16\n```\n\nWithout the `WithLocationSkip` option, the reported location would be line 12, inside the `CreateDatabaseError` function. \nHaving all our errors pointing to this specific line would ne useless.\n\n#### WithStacktrace\n\nUsually the error creation location is enough to get by and find the context during which the error was created, but if \nthe error is created in some central location called from multiple places, it might be useful to have a stacktrace to be \nable to find the caller that led to the error creation.\nFor these cases, use `WithStacktrace`, either when creating the error or when wrapping an existing error. When doing so, \nit will print something like this\n```\nfailure\n\tat .../github.com/quantumcycle/metaerr/errors_test.go:46   //\u003c-- this is the error default location\n    at .../github.com/quantumcycle/metaerr/errors_test.go:64   //\u003c-- this is added by the WithStacktrace option\n    at .../github.com/quantumcycle/metaerr/errors_test.go:297  //\u003c-- this is added by the WithStacktrace option\n```\n\n#### WithContext\n\nThis option allows you to attach a context to the error. Then you can use `StringMetaFromContext` to retrieve data from\nthe context and set some metadata. This is useful if for example you have a user in your context and want to add user\ninformation to each error.","funding_links":[],"categories":["Error Handling","错误处理"],"sub_categories":["Search and Analytic Databases","检索及分析资料库"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquantumcycle%2Fmetaerr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquantumcycle%2Fmetaerr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquantumcycle%2Fmetaerr/lists"}