Reliability
Timeouts, retries, polling, AbortSignal, edge/serverless, streaming reliability, and custom fetch.
This page covers timeout configuration, retry logic, polling options for wait(), AbortSignal support, and edge/serverless considerations.
Timeouts
Every request uses an AbortController-based timeout that is always cleared in a finally block — no timer leaks in serverless or edge environments.
| Context | Default timeout |
|---|---|
| Default / Node / Browser | 60 000 ms (60 s) |
Edge runtime (runtime: 'edge') | 25 000 ms (25 s) |
const client = Skytells(apiKey, {
timeout: 30_000, // 30 seconds
});When the timeout fires: errorId: 'REQUEST_TIMEOUT', httpStatus: 408. The SDK caps timeouts at 2_147_483_647 ms to prevent setTimeout overflow bugs.
Retries
Retries apply only to non-streaming requests. Streaming calls (requestStream(), requestNdjsonStream()) are never retried.
By default, retries: 0 — no automatic retries.
The SDK uses linear backoff: delay = retryDelay × (attempt + 1).
| Attempt | Delay (retryDelay=1000) |
|---|---|
| 1st retry | 1 000 ms |
| 2nd retry | 2 000 ms |
| 3rd retry | 3 000 ms |
There is no per-request retry override. If you need different retry policies for different APIs, create separate clients.
Retries
const client = Skytells(apiKey, {
retry: {
retries: 3,
retryDelay: 1000,
retryOn: [429, 500, 502, 503, 504],
},
});wait() Polling
client.wait(prediction, options?) polls GET /predictions/{id} until the prediction reaches a terminal status (succeeded, failed, cancelled).
intervalnumber
maxWaitnumber
WAIT_TIMEOUT if exceeded.signalAbortSignal
ABORTED.wait() Polling
const pending = await client.predictions.create({
model: 'flux-pro',
input: { prompt: '...' },
});
const result = await client.wait(pending, {
interval: 2000, // poll every 2s
maxWait: 120_000, // give up after 2 min
});AbortSignal
Pass an AbortSignal to stop polling immediately. Throws SkytellsError('ABORTED').
Aborting the signal stops the SDK's polling loop. The prediction continues running server-side unless you separately call prediction.cancel().
AbortSignal
const controller = new AbortController();
setTimeout(() => controller.abort(), 10_000);
try {
const result = await client.wait(pending, {
signal: controller.signal,
});
} catch (e) {
if (e instanceof SkytellsError && e.errorId === 'ABORTED') {
console.log('Cancelled — prediction may still run server-side');
}
}Edge and Serverless
runtime: 'edge'
Edge mode applies:
- Shorter default timeout: 25 000 ms (fits within ~30s wall-clock limits)
- Smaller compat cache: 16 slug entries (vs 64)
- Console hints: Logged once per process
Recommendations
- Always set
maxWait— edge functions have hard wall-clock limits - Always pass
signal— connect it to the request lifecycle - Avoid
wait()on long jobs — usepredict()+ webhooks instead - Keep retries low (0–2) — multiple retries can exceed 30s limits
Edge / Serverless
// app/api/predict/route.ts
import { NextRequest } from 'next/server';
import Skytells from 'skytells';
const client = Skytells(process.env.SKYTELLS_API_KEY!, {
runtime: 'edge',
timeout: 20_000,
fetch: (url, opts) =>
fetch(url, { ...opts, cache: 'no-store' }),
});
export async function POST(req: NextRequest) {
const { prompt } = await req.json();
const pending = await client.predictions.create({
model: 'flux-pro',
input: { prompt },
});
const result = await client.wait(pending, {
maxWait: 15_000,
signal: req.signal,
});
return Response.json({ output: result.output });
}Streaming Reliability
Streaming calls have specific reliability characteristics:
- Not retried: If a stream fails mid-way, the SDK will not restart it.
- Cleanup on abandon: Breaking out of
for await...ofearly still callsreader.cancel()— the response body is released. - Timeout applies: The same
timeoutsetting applies. For long generations, increase it.
try {
for await (const chunk of client.chat.completions.create({
model: 'deepbrain-router',
messages: [{ role: 'user', content: '...' }],
stream: true,
})) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}
} catch (e) {
if (e instanceof SkytellsError) {
if (e.errorId === 'REQUEST_TIMEOUT') {
// Stream took too long — increase timeout
} else if (e.errorId === 'NETWORK_ERROR') {
// Connection dropped — streams are not auto-retried
}
}
}Custom Fetch
Inject a custom fetch for proxying, logging, or testing.
Custom Fetch
const client = Skytells(apiKey, {
fetch: (url, opts) =>
globalThis.fetch(
url.toString().replace('api.skytells.ai', 'my-proxy.example.com'),
opts,
),
});Configuration Reference
| Option | Default (Node) | Default (Edge) | Notes |
|---|---|---|---|
timeout | 60 000 ms | 25 000 ms | Per-request client timeout |
retry.retries | 0 | 0 | Non-streaming only |
retry.retryDelay | 1 000 ms | 1 000 ms | Linear: delay × attempt |
retry.retryOn | [429,500,502,503,504] | same | Status codes triggering retry |
wait.interval | 5 000 ms | 5 000 ms | Poll frequency |
wait.maxWait | undefined | set explicitly! | Total wait budget |
| Cache TTL | 600 000 ms (10 min) | same | Model compat cache |
| Cache max slugs | 64 | 16 | Model compat cache size |
Related
- Client — Client options including
timeout,retry, andruntime - Predictions —
run()andwait()polling with timeout and abort - Errors —
REQUEST_TIMEOUT,WAIT_TIMEOUT,ABORTED,NETWORK_ERRORIDs - Rate Limits — API rate limiting behavior
- Webhooks — Alternative to polling for long-running predictions
- Chat API — Streaming reliability for chat completions
- Responses API — Streaming reliability for responses
- Reference: Client types — Full
ClientOptions,RetryOptions,WaitOptionsdefinitions
How is this guide?