Reference
Errors
Exception hierarchy exported from @alter-ai/alter-sdk.
Every public exception thrown by the SDK extends AlterSDKError. The hierarchy mirrors the Python SDK one-for-one — same class names, same semantics, same fields.
For the canonical error code → exception class mapping and recovery guidance, see Errors.
import { NoDelegatedGrantError, QuotaExceededError, RateLimitError, ScopeReauthRequiredError,} from "@alter-ai/alter-sdk";
const makeRequest = () => app.request("GET", url, { provider: "<provider-id>" });
try { await makeRequest();} catch (error) { if (error instanceof NoDelegatedGrantError) { const session = await app.createConnectSessionForError(error); redirectUser(session.connectUrl); return; } if (error instanceof ScopeReauthRequiredError) { // The user must re-authorize with wider provider scopes. console.warn(`Missing scopes: ${error.missingScopes?.join(", ")}`); } if (error instanceof RateLimitError) { // A transient throttle. `scope` says WHICH ceiling was hit, which decides // the remedy: spreading load across keys helps an `api_key` throttle and // does nothing for an `organization` one. console.warn(`Throttled on ${error.scope ?? "an unnamed ceiling"}`); await new Promise((r) => setTimeout(r, (error.retryAfter ?? 1) * 1000)); return makeRequest(); } if (error instanceof QuotaExceededError) { // Request-quota policy denial — the SDK never auto-retries it. await new Promise((r) => setTimeout(r, (error.retryAfter ?? 60) * 1000)); return makeRequest(); } throw error;}Every name imported above is used by the snippet, so it compiles as-is under
noUnusedLocals. The classes documented below that it does not branch on
(AlterSDKError, GrantNotFoundError, GrantNotDelegableError, …) are
deliberately not imported here — add them when you add a branch for them.
Class hierarchy
Section titled “Class hierarchy”AlterSDKError— root of the hierarchy. Every other exception extends this.AlterValueError— input validation failed at the SDK boundary. Distinct fromBackendErrorso callers can branch on “I passed something bad” vs “the backend said no.”
Backend errors
Section titled “Backend errors”BackendError— generic backend failure.details.status_codeis populated when an HTTP status code is the proximate cause.RestrictedGrantRequiresProxyError— the grant is restricted to the proxy call path; useproxyRequest().SiblingLabelConflictError— the sibling label is already in use on this connection; pick another label.ReAuthRequiredError— the user must re-authorize via Alter Connect. Parent class of the grant-state errors below (GrantExpiredError,GrantRevokedError,GrantDeletedError,CredentialRevokedError), so oneinstanceofcatch covers every “send the user back through Connect” condition.
Grant state
Section titled “Grant state”GrantExpiredError— the grant’s TTL has elapsed. ExposesproviderId,agentId,appUserId, populated only on the delegation-path expiry (a TTL-lapsed delegation chain);undefinedon the root-path TTL expiry.GrantRevokedErrorGrantDeletedErrorGrantNotFoundErrorAgentDelegationMissingError— extendsGrantNotFoundError(so aninstanceof GrantNotFoundErrorcatch still fires). Thrown on the agent path whenrequest(method, url, { grantId })/proxyRequest({ grantId })hits agrant_not_found— the grant is not delegated to this agent, or a user/base grant id was passed where the agent’s own delegation id (listGrants) was required. Recover by delegating the agent through Connect, or resolve by provider (omitgrantId). TheApp/operator path is unaffected.GrantNotDelegableError— extendsBackendError. Thrown byagent.delegate()when the held grant is not delegable, or a managed secret’s operator ceiling blocks re-delegable grants (HTTP 403grant_not_delegable).CredentialRevokedError
Identity resolution
Section titled “Identity resolution”AmbiguousGrantError— multiple grants match the identity + provider tuple. Passaccountor use directgrantIdmode.NoDelegatedGrantError— the calling agent has no delegation for the resolved user on this provider. Recoverable viacreateConnectSessionForError().
Authorization
Section titled “Authorization”PolicyViolationError— backend policy denial.StepUpRequiredError— extendsPolicyViolationError. Acontent_matchstep-up obligation requires a fresher user session.RedactDischargeFailedError— extendsPolicyViolationError. The request was denied because a redact obligation could not be applied safely.InsufficientScopeError— API key lacks the required scope.TokenRefreshInProgressError— concurrent refresh in flight; retry shortly.QuotaExceededError— a request-quota policy window is exhausted. ExposesretryAfter(seconds until the window resets, capped at 2,678,400 seconds / 31 days — a quota window is a calendar period up to a month long, and the response reports the longest exhausted one; may beundefined). A policy denial, not a transient outage — the SDK never auto-retries it.RateLimitError— the platform rate limit for the API key (or the organization-wide aggregate, when configured) is exhausted. A transient throttle, not a denial: waitretryAfterseconds and retry the same request (retryAfteris capped at 3,600 seconds — one hour, above the platform’s legitimate retry guidance). Also exposesscope(which ceiling was hit — a plainstring, not a closed union, so a ceiling added later stays parseable; the known values are exported asRATE_LIMIT_SCOPE_VALUES, currently"ip","api_key"and"organization", ordered outermost-first),limit, andwindowSeconds. Each of those three may beundefined— not every response carrying this error code supplies them, so branch on a missing value rather than assuming one.
Provider
Section titled “Provider”ProviderAPIError— provider returned a 4xx/5xx that is not a scope or credential-rejection failure.ScopeReauthRequiredError— provider returned 403insufficient_scope, or the backend already knew the grant’s scopes are drifted. ExposesmissingScopesandproviderId.ProviderUnauthorizedError— provider returned 401: the credential was revoked, expired, or otherwise invalidated provider-side. ExposesgrantId,providerId,statusCode,responseBody. Recovery: re-authorize an OAuth grant via a new Connect session, or update the stored managed secret. A 401 whoseWWW-Authenticatechallenge carrieserror="insufficient_scope"(RFC 6750 — the credential is still valid, it just lacks a scope) surfaces as a genericProviderAPIErrorinstead.
Connect flow
Section titled “Connect flow”ConnectFlowError— generic flow failure, including an unavailable/expired token on the first poll, an unknown terminal state, or a total usage-limit application failure.ConnectDeniedError— user clicked Deny.ConnectConfigError— provider configuration issue (invalid redirect URI, unknown client, etc.).ConnectTimeoutError— Alter received no completion callback before the local deadline or before a session observed pending expired.details.reasonis"poll_deadline_elapsed"or"session_expired"; without a callback, an exact provider-side error is unknowable.
Approvals
Section titled “Approvals”ApprovalError— base class.ApprovalDeniedErrorApprovalExpiredErrorApprovalTimeoutError— local wait elapsed before any decision. Original transient (when present) is preserved on.cause.ApprovalExecutionFailedError
Managed agents
Section titled “Managed agents”AgentError— base class.InvalidKeyErrorKeyExpiredErrorKeyRotationExpiredErrorKeyInactiveErrorKeyAppNotFoundErrorAgentNotFoundErrorCallerAgentMismatchErrorCallerAgentResolutionErrorAgentNameExistsErrorAgentConcurrentUpdateError— the agent changed after it was read. Fetch it again, recompute the full desired scope allowlist, and retry with the freshversion.AgentInactiveErrorAgentRevokedErrorMeRequiresAgentKeyError—agent.me()was called through an impersonatingAgentreturned byApp.getAgent(), so the request still used an application key rather than a genuine agent key.KeyRevokedErrorKeyAlreadyRevokedErrorKeyNotFoundErrorLastActiveKeyError— would revoke the agent’s only remaining active key. Passforce: trueto override.AgentKeyLimitError— the mint would take the agent past its per-agent active-key cap. Revoke a key it no longer uses, then retry.AgentCannotMintSubagentsErrorIdempotencyKeyBodyMismatchErrorIdempotencyKeyAgentRevokedErrorIdempotencyKeyAgentInactiveError
Network
Section titled “Network”NetworkError— TCP / DNS / socket failure.TimeoutError— extendsNetworkError. Local or remote timeout.
Common fields
Section titled “Common fields”Every AlterSDKError carries details: Record<string, unknown>. toString() returns only the safe message and never stringifies details. Common detail keys:
| Key | Type | Description |
|---|---|---|
status_code | number | HTTP status code from the backend or provider. |
grant_id | string | Grant the call resolved to (when applicable). |
provider_id | string | Provider slug (when applicable). |
method, url | string | HTTP method and pre-injection URL. |
reason | string | Backend-reported reason code. |
retry_after | number | Seconds to wait before retrying — a quota window reset or a rate-limit backoff. QuotaExceededError and RateLimitError also expose it as the typed retryAfter field (may be undefined). On both, the typed field prefers the Retry-After response header over this body value and caps the result — at 3,600 seconds for RateLimitError, at 2,678,400 (31 days) for QuotaExceededError, whose windows are calendar periods — so the two can differ. |
Typed fields
Section titled “Typed fields”The following public fields supplement message, name, and details:
| Class | Fields |
|---|---|
GrantExpiredError | providerId, agentId, appUserId (string | undefined) |
GrantRevokedError | grantId: string | undefined |
CredentialRevokedError | grantId, providerId, appUserId (string | undefined) |
GrantNotFoundError | providerId, agentId, appUserId (string | undefined) |
AmbiguousGrantError | providerId, accountIdentifiers, accountWasProvided, appUserIds, grantIds, candidates |
NoDelegatedGrantError | providerId, agentId, appUserId (string | undefined) |
PolicyViolationError | policyError: string | undefined |
StepUpRequiredError | maxSessionAgeSeconds: number | undefined; inherited policyError is "step_up_required" |
RedactDischargeFailedError | no extra fields; inherited policyError is "redact_discharge_failed" |
SiblingLabelConflictError | label: string | null |
InsufficientScopeError | required, granted, missing, scopeVersion, currentScopeVersion, scopeVersionMismatch, documentationUrl; aliases requiredScopes, grantedScopes, docsUrl |
TokenRefreshInProgressError | grantId: string | undefined |
QuotaExceededError | retryAfter: number | undefined |
ProviderAPIError | statusCode: number | undefined, responseBody: string | undefined |
ScopeReauthRequiredError | grantId, providerId, missingScopes; inherits provider response fields |
ProviderUnauthorizedError | grantId, providerId; inherits provider response fields |
ApprovalError and subclasses | approvalId: string | null |
AgentError and subclasses | code: string, hint?: string |
AmbiguousGrantError.candidates contains GrantCandidate entries with grantId, label, accountIdentifier, and accountDisplayName. The account fields are nullable.
For the typed exception fields and recovery flows for each code, see Errors.