keelwave
SDKsTypeScript

Errors

The typed errors the TypeScript SDK throws, which HTTP status maps to each, and how to handle them.

Every error the SDK raises extends KeelwaveError, so one catch covers the whole SDK.

import { Keelwave, KeelwaveError, KeelwaveRateLimited } from 'keelwave'

const client = new Keelwave({
  apiKey: process.env.KEELWAVE_API_KEY ?? '',
  raiseOnError: true,
})

try {
  await client.ingestAi({ model: 'gpt-4o', status: 'success' })
} catch (err) {
  if (err instanceof KeelwaveRateLimited) {
    console.warn(`rate limited, retry after ${err.retryAfter}s`)
  } else if (err instanceof KeelwaveError) {
    console.warn(`keelwave: ${err.message}`)
  } else {
    throw err
  }
}

With the default raiseOnError: false the ingest methods never throw — they log [keelwave] emit failed: … and continue. The examples on this page assume raiseOnError: true. See Client.

Error types

ClassExtendsRaised when
KeelwaveErrorErrorBase class. Also used to wrap non-keelwave errors when raiseOnError is on.
KeelwaveAuthErrorKeelwaveErrorHTTP 401 — missing, malformed, or revoked API key.
KeelwaveValidationErrorKeelwaveErrorHTTP 400 — the payload failed server-side validation.
KeelwaveRateLimitedKeelwaveErrorHTTP 429 — rate limit hit.
KeelwaveBufferFullKeelwaveErrorHTTP 503 — the server's ingest buffer is full.
KeelwaveServerErrorKeelwaveErrorAny other non-2xx status, including 5xx.
KeelwaveTransportErrorKeelwaveErrorfetch itself failed — DNS, connection refused, timeout.

All seven are exported from the package root:

import {
  KeelwaveError,
  KeelwaveAuthError,
  KeelwaveValidationError,
  KeelwaveRateLimited,
  KeelwaveBufferFull,
  KeelwaveServerError,
  KeelwaveTransportError,
} from 'keelwave'

Each sets name to its class name, so err.name is usable in logs.

Extra properties

Two error types carry more than a message.

KeelwaveRateLimited.retryAfter — the Retry-After response header as a number, or null when the server did not send one.

catch (err) {
  if (err instanceof KeelwaveRateLimited) {
    const waitMs = (err.retryAfter ?? 5) * 1000
    await new Promise((r) => setTimeout(r, waitMs))
  }
}

KeelwaveServerError.status — the HTTP status code that produced the error.

catch (err) {
  if (err instanceof KeelwaveServerError && err.status >= 500) {
    // transient server-side problem, safe to retry
  }
}

Status mapping

The HTTP layer maps responses to types as follows.

StatusError
400KeelwaveValidationError
401KeelwaveAuthError
429KeelwaveRateLimited
503KeelwaveBufferFull
any other 4xx/5xxKeelwaveServerError
network failureKeelwaveTransportError

The message comes from the response body's error field when present, falling back to the HTTP status text.

Which calls throw

CallWith raiseOnError: falseWith raiseOnError: true
client.health()throwsthrows
client.ingestAi()warnsthrows
run.step(), run.toolCall(), run.finish()warnsthrows
client.run() opening the runwarns, uses a local run idthrows
client.observe() / client.agent() emitswarnsthrows
wrapModel() emitsswallowedswallowed

Errors thrown by your own agent code are never intercepted — client.run() marks the run failed with reason error and rethrows.

await expect(
  client.run('test-agent', async () => {
    throw new Error('simulated failure')
  }),
).rejects.toThrow('simulated failure')

Common causes

KeelwaveAuthError — check the key is a full kw_… value and that it matches the server you are pointing at. Keys are per-deployment.

KeelwaveValidationError — usually a field outside its allowed set, for example status on ingestAi must be 'success', 'error', or 'timeout'.

KeelwaveTransportError — the endpoint is wrong or the server is down. The default endpoint is http://localhost:8080; set endpoint if your server lives elsewhere.

KeelwaveBufferFull — the server is ingesting faster than it can flush. Back off and retry; retryAfter is not set on this type, so choose your own delay.

On this page