Raw API failures make models hallucinate. GitHub answers 404 for private repositories the token cannot see, and an expired Vercel OIDC token surfaces as a bare "Not authorized" — both read as "the repo is private" or "permissions are missing" to a model, which then confidently reports the wrong cause.
The SDK maps every failure it can classify to a structured error from an evlog catalog. Each error carries:
code— stable, machine-readable identifier (github_tools.NOT_FOUND)message— what happened, with the GitHub or Connect detail embeddedwhy— the technical cause, including the non-obvious onesfix— the actionable remedylink— the relevant documentation page (this site for auth/Connect setup, GitHub's REST troubleshooting docs for API errors)
Error codes
Authentication and Vercel Connect
| Code | When |
|---|---|
TOKEN_REQUIRED | No token string, provider, or GITHUB_TOKEN env var was available |
OIDC_TOKEN_EXPIRED | VERCEL_OIDC_TOKEN is past its exp claim — thrown before any request is made, with the exact expiry time. Run vercel env pull locally |
CONNECT_NOT_AUTHORIZED | Connect rejected the calling process's identity (403). The request never reached GitHub — this is not a GitHub permission problem |
CONNECT_USER_NOT_CONNECTED | A { type: 'user' } subject was requested but that user has no active GitHub connection |
CONNECT_INSTALLATION_REQUIRED | The connector's GitHub App is not installed on the target account — named in the message when the token targets an org or repository owner, which every tool call with a repository target does |
SUBJECT_CONTEXT_REQUIRED | A connect or connect.subject resolver ran outside a tool execution (no eve context) |
GitHub API
| Code | Status | When |
|---|---|---|
UNAUTHORIZED | 401 | Token invalid, expired, or revoked |
FORBIDDEN | 403 | Missing scope, SAML enforcement, or a user-token-only API (gists, notifications) called with an installation token |
RATE_LIMITED | 403/429 | Rate limit exhausted — the message includes remaining/limit and the reset timestamp |
NOT_FOUND | 404 | The resource does not exist or the token cannot see it — GitHub returns 404 instead of 403 for private resources |
VALIDATION_FAILED | 422 | Well-formed input that GitHub refused, with GitHub's message embedded |
Statuses without a catalog entry (5xx, redirects) pass through unchanged. The original Octokit RequestError stays reachable as cause; request coordinates live in internal and are never serialized toward the model.
What the model sees
In the eve extension, a failing tool returns the structure instead of a flat string:
{
"error": {
"code": "github_tools.NOT_FOUND",
"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/en/rest/using-the-rest-api/troubleshooting-the-rest-api#404-not-found-for-an-existing-resource"
}
}
internal stays off the wire (request URL, raw status). link is included so the model can fetch the docs page when why/fix are not enough.
AI SDK consumers
With generateText/streamText, tool execution errors are thrown and the framework forwards error.message to the model. The catalog messages are written to be self-sufficient for that path. If you need the full structure (error boundaries, logging, custom onError), use evlog's parseError:
import { parseError } from 'evlog'
try {
await generateText({ model, tools, prompt })
}
catch (error) {
const { code, message, why, fix } = parseError(error)
// code autocompletes against the github_tools catalog
}
The catalog itself is exported as githubToolsErrors from @github-tools/sdk for consumers who want to match on specific factories or reuse the codes.
Working context
Default owner/repo/PR/issue/ref on tools, prefer composite reads, and keep payloads lean with detail, includePatch, and line ranges.
Jev from TypeSafe AI
Use Jev from TypeSafe AI to pick GitHub tool presets per request and auto-approve routine writes in AI SDK, TanStack AI, and eve agents.