Libraries
Your package throws new Error('Not Found') when GitHub answers 404. The host catches a string. Its dashboard cannot count it, its support search cannot find it, and the agent that called your tool reads "not found" as "the repository does not exist" when the real cause is a token that cannot see a private repo. The agent retries with a different owner, fails again, opens your docs, and spends a few thousand tokens learning what one sentence in the error could have said.
A catalog puts that sentence in the error. This is what the two versions hand a consumer:
throw new Error('Not Found')
// what the host or the agent receives
// Error: Not Found
throw githubToolsErrors.NOT_FOUND({ detail: 'Not Found' })
// what the host or the agent receives, via parseError() or the tool result
// {
// code: 'github_tools.NOT_FOUND',
// status: 404,
// message: 'GitHub resource not found (404): Not Found',
// why: 'Either the resource does not exist, or the token cannot see it.
// GitHub deliberately returns 404 instead of 403 for private
// resources the token has no access to.',
// fix: 'Check the owner/repo/number input first. If it is correct,
// the token lacks access: grant the repository to the PAT or
// App installation, or use a Connect subject that has access.',
// link: 'https://docs.github.com/.../troubleshooting-the-rest-api#404-not-found-for-an-existing-resource'
// }
The second error is the same 404. It costs the consumer one read instead of a loop, and it costs you the four sentences you would otherwise write in a support reply.
github_tools examples in this section come from @github-tools/sdk, a typed GitHub tool layer for AI agents that ships an evlog catalog of eleven errors. It is a real package with real consumers, so its entries show what a catalog looks like after contact with production. Where a page needs a generic scaffold instead, it uses mylib.Add evlog to my library or SDK
What this section answers
| You are about to… | Read |
|---|---|
| Log from library code without stealing the host's configuration | Emitting Events |
Replace throw new Error() with a catalog the host can route | Structured Errors |
Write why and fix so an agent recovers without opening your docs | Errors Agents Can Act On |
Turn the catalog into a reference page, a JSON file, and an llms.txt section | Exporting the Catalog |
| Convert an existing package with thirty bare throws, in order | Migrating |
| Stop a renamed code or field from breaking a host silently | Testing |
The contract
Your package emits. The host configures. Where events go, which ones survive sampling, and what gets redacted is decided by the application's initLogger() call, and library code never makes that call: initLogger() writes process-wide state shared by every evlog copy of the same major, and the last call wins. A library that calls it at import time replaces the host's drain for the whole process with a single require.
That one rule settles the decisions on Emitting Events: which API you use, where the package name goes, and why a library accepts a request logger as a parameter instead of building one. Nothing on the error pages touches configuration: a catalog is a module of factories, and throwing one is what makes the host's pipeline useful.
What it costs
evlog becomes a peer dependency of your package, so a host that never installed it gets one more install. The process shares one configuration per evlog major, so you pin your peer range to a single major and document it. Each catalog entry is four sentences you maintain: the why and fix go stale when the upstream API changes, and a wrong fix misleads an agent faster than no fix at all. And the emit-only contract is a real constraint: your package cannot route its own events anywhere, which is the point.
For the API underneath this section, Structured Errors covers createError and catalogs as an application uses them, and Wide Events covers the event model.
Node.js request logging
Build a Node.js HTTP handler with structured request logs, explicit outcomes, error context, and a JSON event you can query.
Emitting Events
Emit events from library code the host can attribute and filter: join the host's request, tag your own events, and stay out of the configuration.