mirror of
https://github.com/Skyvern-AI/skyvern.git
synced 2026-04-28 11:40:32 +00:00
346 lines
9.2 KiB
Text
346 lines
9.2 KiB
Text
---
|
|
title: Error Handling
|
|
subtitle: Handle API errors, timeouts, and configure retries
|
|
description: Handle API errors, timeouts, and configure retries in the Skyvern Python SDK. Covers error types, exception handling, HTTP and completion timeouts, retry configuration, and run failure vs API error patterns.
|
|
slug: sdk-reference/error-handling
|
|
keywords:
|
|
- BadRequestError
|
|
- ForbiddenError
|
|
- NotFoundError
|
|
- ConflictError
|
|
- UnprocessableEntityError
|
|
- timeout
|
|
- retry
|
|
- polling
|
|
- run status
|
|
- failure
|
|
---
|
|
|
|
The SDK raises typed exceptions for API errors. In Python, all errors extend `ApiError`. In TypeScript, all errors extend `SkyvernError`. Both include the HTTP status code, response body, and headers.
|
|
|
|
---
|
|
|
|
## Error types
|
|
|
|
| Exception | Status Code | When it's raised |
|
|
|-----------|-------------|------------------|
|
|
| `BadRequestError` | 400 | Invalid request parameters. |
|
|
| `ForbiddenError` | 403 | Invalid or missing API key. |
|
|
| `NotFoundError` | 404 | Resource (run, workflow, session) not found. |
|
|
| `ConflictError` | 409 | Resource conflict (e.g., duplicate creation). |
|
|
| `UnprocessableEntityError` | 422 | Request validation failed. |
|
|
| `ApiError` (Python) / `SkyvernError` (TS) | Any | Base class for all API errors. Catch this as a fallback. |
|
|
| `SkyvernTimeoutError` (TS only) | - | HTTP request timed out. |
|
|
|
|
Import errors from the package:
|
|
|
|
<CodeGroup>
|
|
```python Python
|
|
from skyvern.client.core import ApiError
|
|
from skyvern.client.errors import (
|
|
BadRequestError,
|
|
ForbiddenError,
|
|
NotFoundError,
|
|
ConflictError,
|
|
UnprocessableEntityError,
|
|
)
|
|
```
|
|
|
|
```typescript TypeScript
|
|
import { SkyvernError, SkyvernTimeoutError, SkyvernApi } from "@skyvern/client";
|
|
|
|
// Base errors are top-level exports:
|
|
// SkyvernError - base class for all API errors
|
|
// SkyvernTimeoutError - HTTP request timed out
|
|
|
|
// HTTP status error subclasses are accessed via the SkyvernApi namespace:
|
|
// SkyvernApi.BadRequestError - 400
|
|
// SkyvernApi.ForbiddenError - 403
|
|
// SkyvernApi.NotFoundError - 404
|
|
// SkyvernApi.ConflictError - 409
|
|
// SkyvernApi.UnprocessableEntityError - 422
|
|
```
|
|
</CodeGroup>
|
|
|
|
The specific Python error classes live in `skyvern.client.errors`. The base `ApiError` class lives in `skyvern.client.core`.
|
|
|
|
<Warning>
|
|
**TypeScript:** `SkyvernError` and `SkyvernTimeoutError` are top-level exports. The HTTP-specific errors (`BadRequestError`, etc.) extend `SkyvernError` and are accessed via the `SkyvernApi` namespace.
|
|
</Warning>
|
|
|
|
---
|
|
|
|
## Catching errors
|
|
|
|
<CodeGroup>
|
|
```python Python
|
|
from skyvern import Skyvern
|
|
from skyvern.client.core import ApiError
|
|
from skyvern.client.errors import NotFoundError
|
|
|
|
client = Skyvern(api_key="YOUR_API_KEY")
|
|
|
|
try:
|
|
run = await client.get_run("tsk_nonexistent")
|
|
except NotFoundError as e:
|
|
print(f"Run not found: {e.body}")
|
|
except ApiError as e:
|
|
print(f"API error {e.status_code}: {e.body}")
|
|
```
|
|
|
|
```typescript TypeScript
|
|
import { Skyvern, SkyvernError, SkyvernApi } from "@skyvern/client";
|
|
|
|
const skyvern = new Skyvern({ apiKey: "YOUR_API_KEY" });
|
|
|
|
try {
|
|
const run = await skyvern.getRun("tsk_nonexistent");
|
|
} catch (e) {
|
|
if (e instanceof SkyvernApi.NotFoundError) {
|
|
console.log(`Run not found: ${e.body}`);
|
|
} else if (e instanceof SkyvernError) {
|
|
console.log(`API error ${e.statusCode}: ${e.body}`);
|
|
}
|
|
}
|
|
```
|
|
</CodeGroup>
|
|
|
|
### Error properties
|
|
|
|
Every error has these attributes:
|
|
|
|
| Property (Python) | Property (TS) | Type | Description |
|
|
|-------------------|---------------|------|-------------|
|
|
| `status_code` | `statusCode` | `int \| None` | HTTP status code. |
|
|
| `body` | `body` | `Any` | Response body (usually a dict with error details). |
|
|
| `headers` | - | `dict[str, str] \| None` | Response headers. |
|
|
| - | `rawResponse` | `RawResponse \| undefined` | The raw HTTP response (TS only). |
|
|
| - | `message` | `string` | Human-readable error message (TS only). |
|
|
|
|
---
|
|
|
|
## Timeouts
|
|
|
|
Two different timeouts apply:
|
|
|
|
### HTTP request timeout
|
|
|
|
Controls how long the SDK waits for the HTTP response from the Skyvern API. Set it in the constructor or per-request:
|
|
|
|
<CodeGroup>
|
|
```python Python
|
|
# Global timeout (applies to all requests)
|
|
client = Skyvern(api_key="YOUR_API_KEY", timeout=30.0)
|
|
|
|
# Per-request timeout
|
|
from skyvern.client.core import RequestOptions
|
|
|
|
result = await client.get_run(
|
|
"tsk_abc123",
|
|
request_options=RequestOptions(timeout_in_seconds=10),
|
|
)
|
|
```
|
|
|
|
```typescript TypeScript
|
|
// Global timeout (applies to all requests)
|
|
const skyvern = new Skyvern({
|
|
apiKey: "YOUR_API_KEY",
|
|
timeoutInSeconds: 30,
|
|
});
|
|
|
|
// Per-request timeout
|
|
const result = await skyvern.getRun("tsk_abc123", {
|
|
timeoutInSeconds: 10,
|
|
});
|
|
```
|
|
</CodeGroup>
|
|
|
|
When an HTTP request times out in TypeScript, a `SkyvernTimeoutError` is thrown.
|
|
|
|
### Completion timeout
|
|
|
|
Controls how long `wait_for_completion` / `waitForCompletion` polls before giving up. This is separate from the HTTP timeout:
|
|
|
|
<CodeGroup>
|
|
```python Python
|
|
try:
|
|
result = await client.run_task(
|
|
prompt="Extract data",
|
|
url="https://example.com",
|
|
wait_for_completion=True,
|
|
timeout=300, # Give up after 5 minutes
|
|
)
|
|
except TimeoutError:
|
|
print("Task didn't complete in time")
|
|
```
|
|
|
|
```typescript TypeScript
|
|
try {
|
|
const result = await skyvern.runTask({
|
|
body: {
|
|
prompt: "Extract data",
|
|
url: "https://example.com",
|
|
},
|
|
waitForCompletion: true,
|
|
timeout: 300, // Give up after 5 minutes
|
|
});
|
|
} catch (e) {
|
|
if (e instanceof Error && e.message.includes("Timeout")) {
|
|
console.log("Task didn't complete in time");
|
|
}
|
|
}
|
|
```
|
|
</CodeGroup>
|
|
|
|
The completion timeout raises Python's built-in `TimeoutError` (via `asyncio.timeout`), not `ApiError`. In TypeScript, it throws a standard `Error` with a timeout message.
|
|
|
|
---
|
|
|
|
## Retries
|
|
|
|
Configure automatic retries for transient failures. Set it in the constructor or per-request:
|
|
|
|
<CodeGroup>
|
|
```python Python
|
|
from skyvern.client.core import RequestOptions
|
|
|
|
result = await client.run_task(
|
|
prompt="Extract product data",
|
|
url="https://example.com/products",
|
|
request_options=RequestOptions(max_retries=3),
|
|
)
|
|
```
|
|
|
|
```typescript TypeScript
|
|
// Global retries (default: 2)
|
|
const skyvern = new Skyvern({
|
|
apiKey: "YOUR_API_KEY",
|
|
maxRetries: 3,
|
|
});
|
|
|
|
// Per-request retries
|
|
const result = await skyvern.runTask(
|
|
{
|
|
body: {
|
|
prompt: "Extract product data",
|
|
url: "https://example.com/products",
|
|
},
|
|
},
|
|
{ maxRetries: 5 },
|
|
);
|
|
```
|
|
</CodeGroup>
|
|
|
|
Retries apply to the HTTP request level (network errors, 5xx responses). They do not retry the entire task if it fails at the AI level - use `get_run` / `getRun` to check the status and re-run if needed.
|
|
|
|
---
|
|
|
|
## Abort requests (TypeScript only)
|
|
|
|
Cancel in-flight requests using `AbortSignal`:
|
|
|
|
```typescript
|
|
const controller = new AbortController();
|
|
|
|
// Cancel after 10 seconds
|
|
setTimeout(() => controller.abort(), 10000);
|
|
|
|
try {
|
|
const result = await skyvern.runTask(
|
|
{
|
|
body: {
|
|
prompt: "Extract data",
|
|
url: "https://example.com",
|
|
},
|
|
},
|
|
{ abortSignal: controller.signal },
|
|
);
|
|
} catch (e) {
|
|
if (e instanceof Error && e.name === "AbortError") {
|
|
console.log("Request was aborted");
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Run failure vs API errors
|
|
|
|
There are two distinct failure modes:
|
|
|
|
**API error** - The HTTP request itself failed. The SDK raises an exception.
|
|
|
|
<CodeGroup>
|
|
```python Python
|
|
from skyvern.client.core import ApiError
|
|
|
|
try:
|
|
result = await client.run_task(prompt="...")
|
|
except ApiError as e:
|
|
print(f"API call failed: {e.status_code}")
|
|
```
|
|
|
|
```typescript TypeScript
|
|
import { SkyvernError } from "@skyvern/client";
|
|
|
|
try {
|
|
const result = await skyvern.runTask({
|
|
body: { prompt: "..." },
|
|
});
|
|
} catch (e) {
|
|
if (e instanceof SkyvernError) {
|
|
console.log(`API call failed: ${e.statusCode}`);
|
|
}
|
|
}
|
|
```
|
|
</CodeGroup>
|
|
|
|
**Run failure** - The API call succeeded, but the task/workflow failed during execution. No exception is raised. Check the `status` field:
|
|
|
|
<CodeGroup>
|
|
```python Python
|
|
result = await client.run_task(
|
|
prompt="Fill out the form",
|
|
url="https://example.com",
|
|
wait_for_completion=True,
|
|
)
|
|
|
|
if result.status == "failed":
|
|
print(f"Task failed: {result.failure_reason}")
|
|
elif result.status == "timed_out":
|
|
print(f"Task exceeded step limit after {result.step_count} steps")
|
|
elif result.status == "completed":
|
|
print(f"Success: {result.output}")
|
|
```
|
|
|
|
```typescript TypeScript
|
|
const result = await skyvern.runTask({
|
|
body: {
|
|
prompt: "Fill out the form",
|
|
url: "https://example.com",
|
|
},
|
|
waitForCompletion: true,
|
|
});
|
|
|
|
if (result.status === "failed") {
|
|
console.log(`Task failed: ${result.failure_reason}`);
|
|
} else if (result.status === "timed_out") {
|
|
console.log(`Task exceeded step limit after ${result.step_count} steps`);
|
|
} else if (result.status === "completed") {
|
|
console.log(`Success: ${JSON.stringify(result.output)}`);
|
|
}
|
|
```
|
|
</CodeGroup>
|
|
|
|
### Run statuses
|
|
|
|
| Status | Description |
|
|
|--------|-------------|
|
|
| `created` | Run initialized, not yet queued. |
|
|
| `queued` | Waiting for an available browser. |
|
|
| `running` | AI is executing. |
|
|
| `completed` | Finished successfully. |
|
|
| `failed` | Encountered an error during execution. |
|
|
| `terminated` | Manually stopped. |
|
|
| `timed_out` | Exceeded step limit (`max_steps`). |
|
|
| `canceled` | Canceled before starting. |
|