{"id":38312532,"url":"https://github.com/peake100/pears-go","last_synced_at":"2026-01-17T02:33:14.765Z","repository":{"id":52602294,"uuid":"359249855","full_name":"peake100/pears-go","owner":"peake100","description":"harvest Go errors with ease","archived":false,"fork":false,"pushed_at":"2021-04-23T20:10:52.000Z","size":64,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-20T22:37:45.097Z","etag":null,"topics":["error-handling","errors","go","golang","panic-handler"],"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/peake100.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}},"created_at":"2021-04-18T20:50:25.000Z","updated_at":"2024-06-20T22:37:45.098Z","dependencies_parsed_at":"2022-08-28T14:50:55.286Z","dependency_job_id":null,"html_url":"https://github.com/peake100/pears-go","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":"illuscio-dev/islelib-go","purl":"pkg:github/peake100/pears-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peake100%2Fpears-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peake100%2Fpears-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peake100%2Fpears-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peake100%2Fpears-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/peake100","download_url":"https://codeload.github.com/peake100/pears-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peake100%2Fpears-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28492322,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T00:50:05.742Z","status":"online","status_checked_at":"2026-01-17T02:00:07.808Z","response_time":85,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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","go","golang","panic-handler"],"created_at":"2026-01-17T02:33:14.529Z","updated_at":"2026-01-17T02:33:14.735Z","avatar_url":"https://github.com/peake100.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003ePears\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg height=200 align=\"center\" src=\"https://raw.githubusercontent.com/peake100/pears-go/main/zdocs/source/_static/logo.svg\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003eHarvest Go Errors with Ease\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://dev.azure.com/peake100/Peake100/_build?definitionId=10\"\u003e\u003cimg src=\"https://dev.azure.com/peake100/Peake100/_apis/build/status/pears-go?repoName=peake100%2Fpears-go\u0026branchName=dev\" alt=\"click to see build pipeline\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://dev.azure.com/peake100/Peake100/_build?definitionId=10\"\u003e\u003cimg src=\"https://img.shields.io/azure-devops/tests/peake100/Peake100/10/dev?compact_message\" alt=\"click to see build pipeline\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://dev.azure.com/peake100/Peake100/_build?definitionId=10\"\u003e\u003cimg src=\"https://img.shields.io/azure-devops/coverage/peake100/Peake100/10/dev?compact_message\" alt=\"click to see build pipeline\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/illuscio-dev/islelib-go\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/illuscio-dev/islelib-go\" alt=\"click to see report card\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://codeclimate.com/github/peake100/pears-go/maintainability\"\u003e\u003cimg src=\"https://api.codeclimate.com/v1/badges/eb73ef0f82b6bd72d7a2/maintainability\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/peake100/pears-go\"\u003e\u003cimg src=\"https://img.shields.io/github/go-mod/go-version/peake100/pears-go\" alt=\"Repo\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://pkg.go.dev/github.com/peake100/pears-go?readme=expanded#section-documentation\"\u003e\u003cimg src=\"https://pkg.go.dev/badge/github.com/peake100/pears-go?readme=expanded#section-documentation.svg\" alt=\"Go Reference\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nIntroduction\n------------\n\nPears helps reduce the boilerplate and ensure correctness for common error-handling \nscenarios:\n\n- Panic recovery\n\n- Abort and error collection from concurrent workers.\n\nDemo\n----\n\n**Catch a Panic**\n\n```go\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/peake100/pears-go/pkg/pears\"\n\t\"io\"\n)\n\nfunc main() {\n\t// We can use CatchPanic to catch ay panics that occur in an operation:\n\terr := pears.CatchPanic(func() (innerErr error) {\n\t\t// We are going to throw an io.EOF.\n\t\tpanic(io.EOF)\n\t})\n\n\t// Our error will report that it is from a recovered panic.\n\tfmt.Println(\"Error:\", err)\n\n\t// We can test whether this error is a the result of a panic by using errors.As.\n\tpanicErr := pears.PanicError{}\n\tif errors.As(err, \u0026panicErr) {\n\t\tfmt.Println(\"error is recovered panic\")\n\t\t// do something if this was a panic\n\t}\n\n\t// PanicError implements xerrors.Wrapper, so we can use errors.Is and errors.As\n\t// to get at any inner errors.\n\tif errors.Is(err, io.EOF) {\n\t\tfmt.Println(\"error is io.EOF\")\n\t}\n\n\t// Output:\n\t// Error: panic recovered: EOF\n\t// error is recovered panic\n\t// error is io.EOF\n}\n```\n\n**Gather Errors From Multiple Workers**\n\npears offers a ``Group`` type which takes some inspirations from \n[https://pkg.go.dev/golang.org/x/sync/errgroup](errgroup.Group), with some key \ndifferences:\n\n- All errors are collected, not just the first. Each is wrapped in an OpError and \n  then collected into a GroupErrors. These types offer a number of ways to inspect\n  and resolve errors in concurrent situations.\n\n- Launched operations can be named using GoNamed for more robust error inspection and\n  handling.\n\n- A context is required, and is passed to all child functions, allowing for higher\n  readability of where a context comes from.\n\n- Group must be created with a constructor function: NewGroup.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/peake100/pears-go/pkg/pears\"\n\t\"io\"\n\t\"time\"\n)\n\nfunc main() {\n\tgroup := pears.NewGroup(\n\t\tcontext.Background(), // this context will be used as the parent to al\n\t\t// operation contexts\n\t)\n\n\tfor i := 0; i \u003c 10; i++ {\n\t\t// Each routine will be identified as 'worker [workerNum]'. We do not need to\n\t\t// use the 'go' keyword here. op will be launched as a routine, but some\n\t\t// internal bookkeeping needs to occur before the op can be launched.\n\t\tworkerNum := i\n\t\tgroup.GoNamed(fmt.Sprint(\"worker\", workerNum), func(ctx context.Context) error {\n\t\t\t// We'll use a timer to stand in for some long-running worker.\n\t\t\ttimer := time.NewTimer(5 * time.Second)\n\t\t\tselect {\n\t\t\tcase \u003c-ctx.Done():\n\t\t\t\tfmt.Printf(\"operation %v received abort request\\n\", workerNum)\n\t\t\treturn ctx.Err()\n\t\t\tcase \u003c-timer.C:\n\t\t\t\tfmt.Printf(\"operation %v completed successfully\\n\", workerNum)\n        \treturn nil\n\t\t\t}\n\t\t})\n\t}\n\n\t// Lastly we'll launch a routine that returns an error, which will cancel the\n\t// contexts of every op launched above.\n\tgroup.GoNamed(\"faulty operation\", func(ctx context.Context) error {\n\t\t// This faulty operation will return an io.EOF\n\t\treturn io.EOF\n\t})\n\n\t// Now we join the group, which blocks until all routines launched above return.\n\t// If any operations returned an error, we will get one here.\n\terr := group.Wait()\n\n\t// report our error.\n\tfmt.Println(\"\\nERROR:\", err)\n\n\t// errors.Is() and errors.As() can inspect what caused our operations to fail.\n\t// Because pears.BatchMatchFirst is our error-matching mode, only the FIRST\n\t// encountered error will pass errors.Is() or errors.As().\n\t//\n\t// For us that should be io.EOF.\n\tif errors.Is(err, io.EOF) {\n\t\tfmt.Println(\"error is io.EOF\")\n\t}\n\n\t// Even though the other operations returned context.Canceled, we will NOT\n\t// pass the following check since it was not the FIRST error returned. This is nice\n\t// for checking against an error that started a cascade.\n\t//\n\t// If our match mode had been set to pears.BatchMatchAny, this check would also\n\t// pass\n\tif errors.Is(err, context.Canceled) {\n\t\tfmt.Println(\"error is context.Cancelled\")\n\t}\n\n\t// We can extract a pears.OpError to get more information about the first error.\n\topErr := pears.OpError{}\n\tif !errors.As(err, \u0026opErr) {\n    panic(\"expected opErr\")\n  }\n\n\tfmt.Println(\"batch failure caused by operation:\", opErr.OpName)\n\n\t// We can also extract a GroupErrors to inspect all of our errors more closely:\n\tgroupErrs := pears.GroupErrors{}\n\tif !errors.As(err, \u0026groupErrs) {\n    panic(\"expected BatchErrors\")\n  }\n\n\t// Let's inspect ALL of the errors we got back. We'll see that the context\n\t// cancellation errors were returned, but because of our Batch error matching mode,\n\t// are being kept from surfacing through errors.Is() and errors.As().\n\tfmt.Println(\"\\nALL ERRORS:\")\n\tfor _, thisErr := range groupErrs.Errs {\n\t\tfmt.Println(thisErr)\n\t}\n  \n\t// Unordered Output:\n\t//\n\t// operation 9 received abort request\n\t// operation 8 received abort request\n\t// operation 1 received abort request\n\t// operation 3 received abort request\n\t// operation 6 received abort request\n\t// operation 4 received abort request\n\t// operation 0 received abort request\n\t// operation 7 received abort request\n\t// operation 5 received abort request\n\t// operation 2 received abort request\n\t//\n\t// ERROR: 11 errors returned. first: error during 'faulty operation': EOF\n\t// error is io.EOF\n\t// batch failure caused by operation: faulty operation\n\t//\n\t// ALL ERRORS:\n\t// error during 'faulty operation': EOF\n\t// error during 'worker1': context canceled\n\t// error during 'worker5': context canceled\n\t// error during 'worker7': context canceled\n\t// error during 'worker0': context canceled\n\t// error during 'worker4': context canceled\n\t// error during 'worker6': context canceled\n\t// error during 'worker8': context canceled\n\t// error during 'worker3': context canceled\n\t// error during 'worker2': context canceled\n\t// error during 'worker9': context canceled\n}\n```\n\nGoals\n-----\n\n- Expose simple APIs for dealing with common error-handling situations.\n\n- Support error inspection through errors.Is and errors.As,\n\nNon-Goals\n---------\n\n- Creating complex error frameworks. Pears does not want to re-invent the wheel and \n  seeks only to reduce the boilerplate of leveraging Go's built-in error system.\n  \n- Solving niche problems. This package seeks to help only the most-broad error cases.\n  Features like HTTP or gRPC error-code and serialization systems are beyond the scope \n  of this package.\n\n## Getting Started\nFor API documentation:\n[read the docs](https://pkg.go.dev/github.com/peake100/pears-go?readme=expanded#section-documentation).\n\nFor library development guide, \n[read the docs](https://illuscio-dev.github.io/islelib-go/).\n\n### Prerequisites\n\nGolang 1.6+\n\n## Authors\n\n* **Billy Peake** - *Initial work*\n\n## Attributions\n\n\u003cdiv\u003eLogo made by \u003ca href=\"https://www.freepik.com\" title=\"Freepik\"\u003eFreepik\u003c/a\u003e from \u003ca href=\"https://www.flaticon.com/\" title=\"Flaticon\"\u003ewww.flaticon.com\u003c/a\u003e\u003c/div\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeake100%2Fpears-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeake100%2Fpears-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeake100%2Fpears-go/lists"}