Errors

The error envelope, status codes, and retry behaviour.

The envelope

Every error shares one shape:

1{
2 "statusCode": 401,
3 "timestamp": "2026-08-27T18:57:27.998Z",
4 "path": "/v1/brains",
5 "message": "Invalid API key",
6 "body": {
7 "message": "Invalid API key",
8 "error": "Unauthorized",
9 "statusCode": 401
10 }
11}

body carries the original framework response when available — for validation failures it holds per-field messages, which is usually what you want to surface.

Typed errors

Both SDKs raise a typed error per status, so you can branch without string-matching.

1import { LivingBrain, LivingBrainError } from "@livingbrain/sdk";
2
3try {
4 await client.workspace.getPage({ brainId, slug });
5} catch (err) {
6 if (err instanceof LivingBrain.NotFoundError) {
7 // no such page in this brain
8 } else if (err instanceof LivingBrain.TooManyRequestsError) {
9 // back off and retry
10 } else if (err instanceof LivingBrainError) {
11 console.error(err.statusCode, err.message, err.body);
12 }
13}

Status codes

CodeMeaningWhat to do
400Malformed requestRead body for per-field detail. Don’t retry unchanged.
401Missing API key / Invalid API keyCheck the x-api-key header. See Authentication.
404Not found — or not yoursConfirm the x-subject-id owns this brain.
429Rate limitedBack off and retry; the SDKs do this for you.
5xxServer errorRetried automatically.

A 404 is also what you get for a brain that exists but belongs to a different subject — the API doesn’t distinguish, so it can’t be used to probe for other users’ brains.

Retries

Both SDKs retry 429 and 5xx automatically with exponential backoff. Tune per call:

1await client.brains.list({ maxRetries: 5, timeoutInSeconds: 30 });

400, 401, and 404 are not retried — they won’t succeed on a second attempt.