{"id":13564858,"url":"https://github.com/tomarrell/wrapcheck","last_synced_at":"2025-05-14T01:04:19.875Z","repository":{"id":37476148,"uuid":"282236735","full_name":"tomarrell/wrapcheck","owner":"tomarrell","description":"A Go linter to check that errors from external packages are wrapped","archived":false,"fork":false,"pushed_at":"2025-03-26T10:18:58.000Z","size":161,"stargazers_count":329,"open_issues_count":11,"forks_count":32,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-03T14:17:13.171Z","etag":null,"topics":["check","error","go","go-linter","hacktoberfest","linter"],"latest_commit_sha":null,"homepage":"https://blog.tomarrell.com/post/introducing_wrapcheck_linter_for_go","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/tomarrell.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}},"created_at":"2020-07-24T14:11:28.000Z","updated_at":"2025-03-30T10:35:52.000Z","dependencies_parsed_at":"2024-10-03T10:22:18.945Z","dependency_job_id":"b517ad77-5188-42d2-ad3b-a5305b58cf1d","html_url":"https://github.com/tomarrell/wrapcheck","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomarrell%2Fwrapcheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomarrell%2Fwrapcheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomarrell%2Fwrapcheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomarrell%2Fwrapcheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomarrell","download_url":"https://codeload.github.com/tomarrell/wrapcheck/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248271954,"owners_count":21075801,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["check","error","go","go-linter","hacktoberfest","linter"],"created_at":"2024-08-01T13:01:37.109Z","updated_at":"2025-04-10T18:46:59.425Z","avatar_url":"https://github.com/tomarrell.png","language":"Go","readme":"# Wrapcheck\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/tomarrell/wrapcheck)](https://goreportcard.com/report/github.com/tomarrell/wrapcheck)\n[![Tests](https://github.com/tomarrell/wrapcheck/actions/workflows/test.yaml/badge.svg)](https://github.com/tomarrell/wrapcheck/actions/workflows/test.yaml)\n\nA simple Go linter to check that errors from external packages are wrapped\nduring return to help identify the error source during debugging.\n\n\u003e More detail in [this article](https://blog.tomarrell.com/post/introducing_wrapcheck_linter_for_go)\n\n## Install\n\nGo `\u003e= v1.22.0`\n```bash\n$ go install github.com/tomarrell/wrapcheck/v2/cmd/wrapcheck@v2\n```\n\nWrapcheck is also available as part of the golangci-lint meta linter. Docs and\nusage instructions are available\n[here](https://github.com/golangci/golangci-lint). When used with golangci-lint,\nconfiguration is integrated with the `.golangci.yaml` file.\n\n## Configuration\n\nYou can configure wrapcheck by using a `.wrapcheck.yaml` file in either the\nlocal directory, or in your home directory.\n\n```yaml\n# An array of strings which specify substrings of signatures to ignore. If this\n# set, it will override the default set of ignored signatures. You can find the\n# default set at the top of ./wrapcheck/wrapcheck.go.\nignoreSigs:\n- .Errorf(\n- errors.New(\n- errors.Unwrap(\n- errors.Join(\n- .Wrap(\n- .Wrapf(\n- .WithMessage(\n- .WithMessagef(\n- .WithStack(\n\n# An array of strings specifying additional substrings of signatures to ignore.\n# Unlike ignoreSigs, this option extends the default set (or the set specified\n# in ignoreSigs) without replacing it entirely. This allows you to add specific\n# signatures to the ignore list while retaining the defaults or any items in\n# ignoreSigs.\nextraIgnoreSigs:\n- .CustomError(\n- .SpecificWrap(\n\n# An array of strings which specify regular expressions of signatures to ignore.\n# This is similar to the ignoreSigs configuration above, but gives slightly more\n# flexibility.\nignoreSigRegexps:\n- \\.New.*Error\\(\n\n# An array of glob patterns which, if any match the package of the function\n# returning the error, will skip wrapcheck analysis for this error. This is\n# useful for broadly ignoring packages and/or subpackages from wrapcheck\n# analysis. There are no defaults for this value.\nignorePackageGlobs:\n- encoding/*\n- github.com/pkg/*\n\n# ignoreInterfaceRegexps defines a list of regular expressions which, if matched\n# to a underlying interface name, will ignore unwrapped errors returned from a\n# function whose call is defined on the given interface.\nignoreInterfaceRegexps:\n- ^(?i)c(?-i)ach(ing|e)\n\n# ReportInternalErrors determines whether wrapcheck should report errors returned\n# from inside the package.\nreportInternalErrors: true\n```\n\n## Usage\n\nTo lint all the packages in a program:\n\n```bash\n$ wrapcheck ./...\n```\n\n## Testing\n\nThis linter is tested using `analysistest`, you can view all the test cases\nunder the [testdata](./wrapcheck/testdata) directory.\n\n## TLDR\n\nIf you've ever been debugging your Go program, and you've seen an error like\nthis pop up in your logs.\n\n```log\ntime=\"2020-08-04T11:36:27+02:00\" level=error error=\"sql: error no rows\"\n```\n\nThen you know exactly how painful it can be to hunt down the cause when you have\nmany methods which looks just like the following:\n\n```go\nfunc (db *DB) getUserByID(userID string) (User, error) {\n\tsql := `SELECT * FROM user WHERE id = $1;`\n\n\tvar u User\n\tif err := db.conn.Get(\u0026u, sql, userID); err != nil {\n\t\treturn User{}, err // wrapcheck error: error returned from external package is unwrapped\n\t}\n\n\treturn u, nil\n}\n\nfunc (db *DB) getItemByID(itemID string) (Item, error) {\n\tsql := `SELECT * FROM item WHERE id = $1;`\n\n\tvar i Item\n\tif err := db.conn.Get(\u0026i, sql, itemID); err != nil {\n\t\treturn Item{}, err // wrapcheck error: error returned from external package is unwrapped\n\t}\n\n\treturn i, nil\n}\n```\n\nThe problem here is that multiple method calls into the `sql` package can return\nthe same error. Therefore, it helps to establish a trace point at the point\nwhere error handing across package boundaries occurs.\n\nTo resolve this, simply wrap the error returned by the `db.Conn.Get()` call.\n\n```go\nfunc (db *DB) getUserByID(userID string) (User, error) {\n\tsql := `SELECT * FROM user WHERE id = $1;`\n\n\tvar u User\n\tif err := db.Conn.Get(\u0026u, sql, userID); err != nil {\n\t\treturn User{}, fmt.Errorf(\"failed to get user by ID: %v\", err) // No error!\n\t}\n\n\treturn u, nil\n}\n\nfunc (db *DB) getItemByID(itemID string) (Item, error) {\n\tsql := `SELECT * FROM item WHERE id = $1;`\n\n\tvar i Item\n\tif err := db.Conn.Get(\u0026i, sql, itemID); err != nil {\n\t\treturn Item{}, fmt.Errorf(\"failed to get item by ID: %v\", err) // No error!\n\t}\n\n\treturn i, nil\n}\n```\n\nNow, your logs will be more descriptive, and allow you to easily locate the\nsource of your errors.\n\n```log\ntime=\"2020-08-04T11:36:27+02:00\" level=error error=\"failed to get user by ID: sql: error no rows\"\n```\n\nA further step would be to enforce adding stack traces to your errors instead\nusing\n[`errors.WithStack()`](https://pkg.go.dev/github.com/pkg/errors?tab=doc#WithStack)\nhowever, enforcing this is out of scope for this linter for now.\n\n## Why?\n\nErrors in Go are simple values. They contain no more information about than the\nminimum to satisfy the interface:\n\n```go\ntype Error interface {\n  Error() string\n}\n```\n\nThis is a fantastic feature, but can also be a limitation. Specifically when you\nare attempting to identify the source of an error in your program.\n\nAs of Go 1.13, error wrapping using `fmt.Errorf(...)` is the recommend way to\ncompose errors in Go in order to add additional information.\n\nErrors generated by your own code are usually predictable. However, when you\nhave a few frequently used libraries (think `sqlx` for example), you may run\ninto the dilemma of identifying exactly where in your program these errors are\ncaused.\n\nIn other words, you want a call stack.\n\nThis is especially apparent if you are a diligent Gopher and always hand your\nerrors back up the call stack, logging at the top level.\n\nSo how can we solve this?\n\n## Solution\n\nWrapping errors at the call site.\n\nWhen we call into external libraries which may return an error, we can wrap the\nerror to add additional information about the call site.\n\ne.g.\n\n```go\n...\n\nfunc (db *DB) createUser(name, email, city string) error {\n  sql := `INSERT INTO customer (name, email, city) VALUES ($1, $2, $3);`\n\n  if _, err := tx.Exec(sql, name, email, city); err != nil {\n    // %v verb preferred to prevent error becoming part of external API\n    return fmt.Errorf(\"failed to insert user: %v\", err)\n  }\n\n  return nil\n}\n\n...\n```\n\nThis solution allows you to add context which will be handed to the caller,\nmaking identifying the source easier during debugging.\n\n## Contributing\n\nAs with most static analysis tools, this linter will likely miss some obscure\ncases. If you come across a case which you think should be covered and isn't,\nplease file an issue including a minimum reproducible example of the case.\n\n## License\n\nThis project is licensed under the MIT license. See the [LICENSE](./LICENSE) file for more\ndetails.\n\n","funding_links":[],"categories":["Code Analysis","Go","Recently Updated","Repositories","代码分析"],"sub_categories":["Routers","[Sep 15, 2024](/content/2024/09/15/README.md)","路由器"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomarrell%2Fwrapcheck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomarrell%2Fwrapcheck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomarrell%2Fwrapcheck/lists"}