> ## Documentation Index
> Fetch the complete documentation index at: https://docs.triqai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Handle API errors gracefully in your application

This guide covers how to handle errors from the Triqai API, including common error codes, troubleshooting tips, and best practices.

## Error Response Format

All error responses follow a consistent structure:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "error_code",
    "message": "Human-readable description",
    "details": {
      /* optional additional info */
    }
  },
  "meta": {
    "generatedAt": "2026-01-19T10:30:00Z",
    "requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a123",
    "version": "1.3.13"
  }
}
```

## Error Codes

### Authentication Errors (401)

| Code                   | Message                    | Solution                       |
| ---------------------- | -------------------------- | ------------------------------ |
| `authentication_error` | Invalid or missing API key | Check your `X-API-Key` header  |
| `authentication_error` | Invalid API key format     | Ensure key starts with `triq_` |

```typescript theme={null}
import { AuthenticationError } from "triqai";

try {
  await triqai.transactions.enrich({
    title: "TEST",
    country: "US",
    type: "expense",
  });
} catch (err) {
  if (err instanceof AuthenticationError) {
    console.error("API key invalid. Please check your configuration.");
  }
}
```

### Payment Errors (402)

| Code                   | Message              | Solution                        |
| ---------------------- | -------------------- | ------------------------------- |
| `insufficient_credits` | Insufficient credits | Enable overages or upgrade plan |

```typescript theme={null}
import { InsufficientCreditsError } from "triqai";

try {
  await triqai.transactions.enrich({
    title: "TEST",
    country: "US",
    type: "expense",
  });
} catch (err) {
  if (err instanceof InsufficientCreditsError) {
    console.log("Top up credits at https://triqai.com/dashboard");
  }
}
```

### Not Found Errors (404)

| Code        | Message            | Solution             |
| ----------- | ------------------ | -------------------- |
| `not_found` | Resource not found | Verify the ID exists |

```typescript theme={null}
import { NotFoundError } from "triqai";

try {
  await triqai.transactions.get("nonexistent-id");
} catch (err) {
  if (err instanceof NotFoundError) {
    console.error("Resource not found");
  }
}
```

### Validation Errors (422)

| Code               | Message           | Details                                    |
| ------------------ | ----------------- | ------------------------------------------ |
| `validation_error` | Validation failed | `fieldErrors` object with per-field errors |

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "Validation failed",
    "details": {
      "fieldErrors": {
        "title": ["Title is required"],
        "country": ["Invalid country code. Use ISO 3166-1 alpha-2 format."],
        "type": ["Type must be 'expense' or 'income'"]
      }
    }
  }
}
```

```typescript theme={null}
import { ValidationError } from "triqai";

try {
  await triqai.transactions.enrich({
    title: "",
    country: "INVALID",
    type: "expense",
  });
} catch (err) {
  if (err instanceof ValidationError) {
    console.log("Field errors:", err.fieldErrors);
    // { title: ["Title is required"], country: ["Invalid country code"] }
  }
}
```

### Rate Limit Errors (429)

| Code           | Message             | Headers                        |
| -------------- | ------------------- | ------------------------------ |
| `rate_limited` | Rate limit exceeded | `Retry-After`, `X-RateLimit-*` |

```typescript theme={null}
import { RateLimitError } from "triqai";

try {
  await triqai.transactions.enrich({
    title: "TEST",
    country: "US",
    type: "expense",
  });
} catch (err) {
  if (err instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${err.rateLimitInfo.retryAfter}s`);
  }
}
```

<Tip>
  Rate-limited requests are automatically retried with exponential backoff, so
  you typically don't need to handle 429 errors manually.
</Tip>

### Server Errors (500)

| Code             | Message                      | Solution                       |
| ---------------- | ---------------------------- | ------------------------------ |
| `internal_error` | An unexpected error occurred | Retry with exponential backoff |

```typescript theme={null}
import { InternalServerError } from "triqai";

// 500 errors are retried automatically (up to 3 times by default).
// You can customize this behavior:
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
  maxRetries: 5,
  retryDelay: 1000,
});
```

## Automatic Retries

Retries are handled automatically with exponential backoff. You can customize the retry behavior:

```typescript theme={null}
import Triqai from "triqai";

const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
  maxRetries: 3, // max retry attempts (default: 3)
  retryDelay: 500, // base delay in ms (default: 500)
  maxRetryDelay: 30_000, // max delay cap (default: 30000)
});
```

**Retry behavior:**

* `GET` and `DELETE` requests are always retried on transient errors
* `POST` requests are only retried when an `idempotencyKey` is provided
* The `Retry-After` header from 429 responses is respected automatically
* Retried status codes: 429, 500, 503, 504, and network errors

To disable retries entirely:

```typescript theme={null}
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, { maxRetries: 0 });
```

## Complete Error Handling Pattern

Here's a complete error handling example using typed error classes:

```typescript theme={null}
import Triqai, {
  TriqaiError,
  AuthenticationError,
  ValidationError,
  RateLimitError,
  InsufficientCreditsError,
  NotFoundError,
} from "triqai";

const triqai = new Triqai(process.env.TRIQAI_API_KEY!);

try {
  const result = await triqai.transactions.enrich({
    title: "STARBUCKS NYC",
    country: "US",
    type: "expense",
  });
} catch (err) {
  if (err instanceof AuthenticationError) {
    console.error("Invalid API key — check your configuration");
  } else if (err instanceof ValidationError) {
    console.log("Validation errors:", err.fieldErrors);
  } else if (err instanceof RateLimitError) {
    console.log(`Rate limited — retry after ${err.rateLimitInfo.retryAfter}s`);
  } else if (err instanceof InsufficientCreditsError) {
    console.log("No credits remaining — top up at triqai.com/dashboard");
  } else if (err instanceof NotFoundError) {
    console.log("Resource not found");
  } else if (err instanceof TriqaiError) {
    console.log(`API error ${err.statusCode}: ${err.message} [${err.code}]`);
    console.log("Request ID:", err.requestId);
  }
}
```

See the [error handling reference](/sdk/error-handling) for the full list of error classes.

## Logging and Monitoring

### Request Logging

Use built-in debug hooks for observability:

```typescript theme={null}
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
  onRequest: (info) => {
    console.log(`→ ${info.method} ${info.url}`);
  },
  onResponse: (info) => {
    console.log(`← ${info.status} in ${info.durationMs}ms`);
  },
});
```

Or wrap calls for custom logging:

```typescript theme={null}
async function enrichWithLogging(transaction: {
  title: string;
  country: string;
  type: string;
}) {
  const startTime = Date.now();

  try {
    const result = await triqai.transactions.enrich(transaction);
    console.log({
      event: "enrichment_success",
      duration: Date.now() - startTime,
    });
    return result;
  } catch (err) {
    if (err instanceof TriqaiError) {
      console.error({
        event: "enrichment_error",
        code: err.code,
        requestId: err.requestId,
      });
    }
    throw err;
  }
}
```

### Error Tracking

```typescript theme={null}
import { TriqaiError } from "triqai";

function trackError(error: TriqaiError, context: Record<string, unknown> = {}) {
  errorTracker.captureException(error, {
    tags: {
      service: "triqai",
      errorCode: error.code,
    },
    extra: {
      requestId: error.requestId,
      statusCode: error.statusCode,
      ...context,
    },
  });
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always include requestId in logs" icon="bookmark">
    The `meta.requestId` helps with debugging and support requests.
  </Accordion>

  <Accordion title="Implement circuit breakers for high-volume" icon="toggle-off">
    If errors persist, temporarily stop requests to prevent cascading failures.
  </Accordion>

  <Accordion title="Handle partial results separately" icon="circle-half-stroke">
    Partial results (`partial: true`) are successes with some failures handle
    them differently from full failures.
  </Accordion>

  <Accordion title="Validate input before sending" icon="shield-check">
    Catch validation errors client-side when possible to reduce failed API
    calls.
  </Accordion>

  <Accordion title="Use structured error classes" icon="code">
    Create error classes that make it easy to check error types and extract
    details.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Complete API documentation with error codes
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/platform/rate-limits">
    Understand and manage rate limits
  </Card>
</CardGroup>
