{"id":13527009,"url":"https://github.com/tc39/proposal-error-cause","last_synced_at":"2025-04-01T09:30:57.517Z","repository":{"id":44903103,"uuid":"292781184","full_name":"tc39/proposal-error-cause","owner":"tc39","description":"TC39 proposal for accumulating errors","archived":true,"fork":false,"pushed_at":"2021-10-26T17:27:58.000Z","size":167,"stargazers_count":380,"open_issues_count":0,"forks_count":9,"subscribers_count":25,"default_branch":"main","last_synced_at":"2024-11-02T12:33:53.600Z","etag":null,"topics":["proposal","tc39"],"latest_commit_sha":null,"homepage":"https://tc39.es/proposal-error-cause/","language":"HTML","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/tc39.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":"2020-09-04T07:32:35.000Z","updated_at":"2024-10-24T03:51:42.000Z","dependencies_parsed_at":"2022-09-01T17:01:28.425Z","dependency_job_id":null,"html_url":"https://github.com/tc39/proposal-error-cause","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":"tc39/template-for-proposals","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-error-cause","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-error-cause/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-error-cause/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-error-cause/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tc39","download_url":"https://codeload.github.com/tc39/proposal-error-cause/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246615916,"owners_count":20806026,"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":["proposal","tc39"],"created_at":"2024-08-01T06:01:39.236Z","updated_at":"2025-04-01T09:30:57.114Z","avatar_url":"https://github.com/tc39.png","language":"HTML","funding_links":[],"categories":["HTML"],"sub_categories":[],"readme":"# Error Cause\n\nStatus: Stage 4\n\nAuthor: [@legendecas](https://github.com/legendecas)\n\nChampion: [@legendecas](https://github.com/legendecas), [@hemanth](https://github.com/hemanth)\n\n## Chaining Errors\n\nErrors will be constructed to represent runtime abnormalities. To help\nunexpected behavior diagnosis, errors need to be augmented with contextual\ninformation like error messages, error instance properties to explain what\nhappened at the time.\n\nIf the error were thrown from deep internal methods, the thrown error may not\nbe straightforward to be easily conducted without proper exception design\npattern. Catching an error and throwing it with additional contextual data is\na common approach of error handling pattern. Multiple methods are available to\naugment the caught error with additional contextual information:\n\n```js\nasync function doJob() {\n  const rawResource = await fetch('//domain/resource-a')\n    .catch(err =\u003e {\n      // How to wrap the error properly?\n      // 1. throw new Error('Download raw resource failed: ' + err.message);\n      // 2. const wrapErr = new Error('Download raw resource failed');\n      //    wrapErr.cause = err;\n      //    throw wrapErr;\n      // 3. class CustomError extends Error {\n      //      constructor(msg, cause) {\n      //        super(msg);\n      //        this.cause = cause;\n      //      }\n      //    }\n      //    throw new CustomError('Download raw resource failed', err);\n    })\n  const jobResult = doComputationalHeavyJob(rawResource);\n  await fetch('//domain/upload', { method: 'POST', body: jobResult });\n}\n\nawait doJob(); // =\u003e TypeError: Failed to fetch\n```\n\nIf the errors were chained with causes, it can be greatly helpful to diagnosing\nunexpected exceptions. As the example above shows, quite a few laboring works\nhas to be done for a simple error handling case to augmenting the caught\nerror with contextual message. Besides, no consensus on which property will\nrepresenting the cause makes it not feasible for developer tools to revealing\ncontextual information of causes.\n\nThe proposed solution is adding an additional options parameter to the `Error()`\nconstructor with a `cause` property, the value of which will be assigned\nto the error instances as a property. So errors can be chained without\nunnecessary and overelaborate formalities on wrapping the errors in\nconditions.\n\n```js\nasync function doJob() {\n  const rawResource = await fetch('//domain/resource-a')\n    .catch(err =\u003e {\n      throw new Error('Download raw resource failed', { cause: err });\n    });\n  const jobResult = doComputationalHeavyJob(rawResource);\n  await fetch('//domain/upload', { method: 'POST', body: jobResult })\n    .catch(err =\u003e {\n      throw new Error('Upload job result failed', { cause: err });\n    });\n}\n\ntry {\n  await doJob();\n} catch (e) {\n  console.log(e);\n  console.log('Caused by', e.cause);\n}\n// Error: Upload job result failed\n// Caused by TypeError: Failed to fetch\n```\n\n## Compatibilities\n\nIn Firefox, `Error()` constructor can receive two optional additional\npositional parameters: `fileName`, `lineNumber`. Those parameters will be\nassigned to newly constructed error instances with the name `fileName` and\n`lineNumber` respectively.\n\nHowever, no standard on either ECMAScript or Web were defined on such behavior.\nSince the second parameter under this proposal must be an object with a `cause`\nproperty, it will be distinguishable from a string.\n\n## Implementations\n\nPolyfills:\n- [error-cause](https://www.npmjs.com/package/error-cause): An [es-shim] interface polyfill.\n- [Pony Cause](https://github.com/voxpelli/pony-cause): Helpers to work with ECMAScript error cause and [VError] style error cause.\n\nJavaScript Environments:\n- Chrome, released on 93,\n- Firefox, released on 91,\n- Safari, released on 15,\n- Node.js, released on [v16.9.0](https://nodejs.org/en/blog/release/v16.9.0/#error-cause).\n\n## FAQs\n\n### Differences with `AggregateError`\n\nThe key difference between those two is that the errors in the `AggregateError`\ndoesn't have to be related. `AggregateError` are just a bunch of errors just\nhappened to be caught and aggregated in one place, they can be totally\nunrelated. E.g. `jobA` and `jobB` can do nothing to each other in\n`Promise.allSettled([ jobA, jobB ])`. However, if an error were thrown from\nseveral levels deep of `jobA`, the `cause` property can accumulate context\ninformation of those levels to help understanding what happened exactly.\n\nWith `AggregateError`, we get breadth. With the `cause` property, we get depth.\n\n### Why not custom subclasses of `Error`\n\nWhile there are lots of ways to achieve the behavior of the proposal, if the\n`cause` property is explicitly defined by the language, debug tooling can\nreliably use this info rather than contracting with developers to construct an\nerror properly.\n\n[es-shim]: https://github.com/es-shims/api\n[VError]: https://github.com/joyent/node-verror\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-error-cause","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftc39%2Fproposal-error-cause","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-error-cause/lists"}