Skip to main content

Error Handling

The client provides comprehensive error handling with typed error responses.

Response Types

Every request returns a result that can be one of three types:

  1. Success Response - Request succeeded
  2. Error Response - Server returned an error response
  3. Unexpected Error Response - Network, validation, or unexpected error

Success Response

const result = await client.get("/users/{id}", {
params: { id: "123" },
});

if (result.success) {
// result.body is typed based on your output schema
console.log(result.body);
// result.code contains the status code (e.g., "200", "201")
}

Error Response

When the server returns an error response (non-2xx status):

const result = await client.post("/users", {
body: { name: "Alice" },
});

if (!result.success && typeof result.code === "string") {
// Server error response (typed error from Response schemas)
// result.error is typed based on the error schema for this status code
console.error(result.code); // HTTP status code (e.g., "404", "500")
console.error(result.error); // Typed error body based on schema
}

Unexpected Error Response

For network errors, validation errors, or other unexpected issues:

const result = await client.get("/users/{id}", {
params: { id: "123" },
});

if (!result.success && typeof result.code === "number") {
// Unexpected error response (not defined in Response schemas)
if (result.error instanceof Error) {
console.error(result.error.message); // Error message
// error may be UnexpectedApiClientError with additional properties
} else {
console.error("Unexpected error:", result.error);
}
console.error(result.code); // HTTP status code as number
}

Error Classes

The client exports error classes for programmatic handling:

ClassDescription
ApiClientErrorBase class for all client errors
ValidationErrorRequest/response validation failed
UnexpectedApiClientErrorNetwork error or unexpected response
TimeoutErrorRequest exceeded timeout
import {
ValidationError,
UnexpectedApiClientError,
TimeoutError,
ApiClientError,
} from "@alt-stack/http-client-fetch";

try {
await client.get("/users/{id}", {
params: { id: 123 },
});
} catch (error) {
if (error instanceof ValidationError) {
console.error("Validation error:", error.validationErrors);
} else if (error instanceof TimeoutError) {
console.error(`Request timed out after ${error.timeout}ms`);
} else if (error instanceof UnexpectedApiClientError) {
console.error("Request failed:", error.message, error.code);
} else if (error instanceof ApiClientError) {
console.error("Client error:", error.message);
}
}

Retry Logic

The client includes built-in retry logic with exponential backoff:

const result = await client.get("/users/{id}", {
params: { id: "123" },
retries: 3, // Will retry up to 3 times
});

By default, retries are automatically performed for:

  • Network errors

Retries are not performed by default for:

  • Validation errors
  • Client errors (4xx status codes)
  • Server errors (5xx status codes) - these are valid HTTP responses

Custom Retry Logic

Use the shouldRetry option to customize retry behavior:

const result = await client.get("/users/{id}", {
params: { id: "123" },
retries: 3,
shouldRetry: ({ attempt, error, response }) => {
// Retry on 5xx server errors
if (response?.status >= 500) return true;
// Retry on rate limiting
if (response?.status === 429) return true;
// Retry on network errors
if (error) return true;
return false;
},
});

The shouldRetry callback receives a context object with:

  • attempt - Current attempt number (0-indexed)
  • error - Error thrown during request (network errors, timeouts)
  • response - HTTP response received (status, statusText, data)

Examples

Retry on 5xx server errors:

shouldRetry: ({ response }) => response?.status !== undefined && response.status >= 500

Limit retries regardless of retries option:

shouldRetry: ({ attempt }) => attempt < 2

Retry on specific error codes:

shouldRetry: ({ response }) => {
const retryableCodes = [500, 502, 503, 504, 429];
return response?.status !== undefined && retryableCodes.includes(response.status);
}

Custom logic combining error and response:

shouldRetry: ({ error, response }) => {
// Always retry network errors
if (error) return true;
// Retry rate limits and server errors
if (response?.status === 429 || (response?.status ?? 0) >= 500) return true;
return false;
}

Timeouts

Set a timeout for requests:

const result = await client.get("/users/{id}", {
params: { id: "123" },
timeout: 5000, // 5 seconds
});

If the request exceeds the timeout, a TimeoutError is thrown with the timeout property indicating the configured timeout in milliseconds.