Skip to content

Errors

Every failure in the SDK is a thrown EdgibleError subclass. There are no error-shaped return values, no null fallbacks, and no need to parse message text: each error carries a stable machine-readable code, the backend requestId for support, and an honest retryable flag the SDK’s own retry layer already acts on.

All classes extend EdgibleError and are exported from @edgible-team/sdk.

ClassFires onDefault coderetryable
ValidationError400 (and unexpected 4xx)VALIDATION_FAILEDno
AuthenticationError401; missing/expired credentialsAUTH_INVALID_CREDENTIALS, AUTH_NO_CREDENTIALS, AUTH_TOKEN_EXPIREDno
AuthorizationError403 (authenticated, not permitted)FORBIDDENno
NotFoundError404NOT_FOUNDno
ConflictError409 (already exists, wrong state for the operation)CONFLICTno
RateLimitError429RATE_LIMITEDyes
ServerError5xxSERVER_ERRORyes
NetworkErrortransport failure (DNS, connection reset, …)NETWORK_ERRORyes
TimeoutErrorrequest aborted / deadline exceededREQUEST_TIMEOUTyes
ConfigurationErrorclient-side misconfig (bad env, empty baseUrl, unsupported credential)CONFIG_INVALID, CONFIG_MISSING_BASE_URL, SERVICE_ACCOUNT_NOT_SUPPORTEDno
WaiterErrora long-running operation failed or timed out while being waited onWAITER_FAILED, WAITER_TIMEOUTno
class EdgibleError extends Error {
readonly code: EdgibleErrorCode; // stable, machine-readable — branch on this
readonly category: ErrorCategory; // coarse class: 'validation' | 'not_found' | 'rate_limit' | …
readonly statusCode: number | undefined; // HTTP status, when the error came from a response
readonly requestId: string | undefined; // backend trace id — include in bug reports
readonly retryable: boolean; // could a retry succeed?
readonly details: unknown; // structured context (e.g. validation field errors)
readonly cause: unknown; // the original error, preserved
}

Two subclasses add a field:

  • RateLimitError.retryAfterMs — parsed from the response’s Retry-After header when present.
  • WaiterError.lastState — the last observed operation state ({ phase, terminal, raw }).

A note on code: when the backend supplies a code in the error body, it passes through verbatim; otherwise the SDK assigns the default for the status (table above). EdgibleErrorCode is an open string union — the known set autocompletes, but always be prepared for resource-specific codes. Branch on code or instanceof, never on message — messages may change between releases; codes do not.

Use instanceof for the coarse class, code when you need finer grain:

import {
EdgibleError,
NotFoundError,
ValidationError,
} from '@edgible-team/sdk';
try {
await client.applications.delete(appId);
} catch (err) {
if (err instanceof NotFoundError) {
// Already gone — fine for a delete.
return;
}
if (err instanceof ValidationError) {
// Bad input; structured context (e.g. field errors) is on `details`.
console.error('Invalid request:', err.message, err.details);
return;
}
if (err instanceof EdgibleError && err.retryable) {
// ServerError / NetworkError / TimeoutError / RateLimitError that the
// SDK already retried (see below) and still failed. Queue it, alert,
// or surface to the user — don't tight-loop another retry.
console.error(`Transient failure [${err.code}], requestId=${err.requestId}`);
throw err;
}
throw err; // anything else is a real bug or a permission problem
}

When reporting a problem, always include err.requestId — it is the backend trace ID and lets operators find the exact request in logs.

A retry layer is built into every client, so do not wrap SDK calls in your own retry loop for transient failures — you would multiply the backoff.

  • What is retried: only errors with retryable: trueServerError (5xx), NetworkError, TimeoutError, and RateLimitError (429). All other errors fail fast on the first attempt.
  • How: up to 4 attempts total, exponential backoff with full jitter (random delay between 0 and the current backoff ceiling), base delay 200 ms, capped at 10 s per wait.
  • 429s: when the response carries a Retry-After header, that delay is honoured (capped at the max delay) instead of the jittered backoff.

If a retryable error reaches your code, the SDK has already exhausted its attempts. Tune the policy per client:

const client = createClient({
retry: { maxAttempts: 6, baseDelayMs: 500, maxDelayMs: 30_000 },
});

One related behaviour lives outside the retry policy: on a 401, the auth layer performs exactly one token refresh and replays the request once before surfacing an AuthenticationError. See Authentication & credentials.

Helpers that wait for an operation to finish (deploys, provisioning, migrations) throw WaiterError when the operation fails or doesn’t reach the expected state in time. It is never retried automatically, and it carries the last state the waiter observed:

catch (err) {
if (err instanceof WaiterError) {
console.error(`Stuck in phase '${err.lastState.phase}'`, err.lastState.raw);
}
}

See Long-running operations for waiters, timeouts, and progress callbacks.