Failure behaviour
When we cannot be reached, the SDK builds the request from a local tail of recent turns and your model call goes ahead. Memory never fails the model call.
Memory must never be the reason your agent stops answering. The SDKs are built around that.
decorate falls back to the local tail
The SDK keeps a local tail for each memory, in your process: your instructions and the newest turns of the last request, up to a size you set. When decorate cannot get an answer from us, it returns a request built from that tail plus the new messages, and your model call goes ahead.
It falls back on:
- a network error or a timeout (15 seconds by default,
timeoutMs/timeout); - a
5xxresponse; 429(rate limited, or the plan's quota used up) and408.
Every fallback is reported to your onError callback (on_error in Python) with the operation and memory id, so you can log it.
It does not fall back on other 4xx responses. A malformed request, a revoked key or an unknown memory id is a bug for you to see, so decorate throws a ThreadlessError with the status, a code, a message and a hint saying what to do about it.
const memory = new Threadless({
timeoutMs: 15_000,
tailTokens: 60_000,
onError: (error, { operation, memoryId }) => log.warn({ error, operation, memoryId }),
});memory = Threadless(
timeout=15.0,
tail_tokens=60_000,
on_error=lambda error, context: log.warning("threadless %s %s", context, error),
)What the tail holds
- The latest instructions you sent.
- The newest history that fits in
tailTokens(60,000 estimated tokens by default), cut at a user message so a tool call is never separated from its output. - If not even one turn fits, the newest user message onward.
The tail is refreshed from every successful decorate, so it mirrors what we last sent you, facts block and recall included. It lives in memory only: a process that restarts has no tail, and the first fallback after a restart carries just the new messages and instructions. The SDK keeps tails for the 200 most recently used memories.
While the fallback is in use, nothing is forgotten, no facts are added and nothing is recalled; the history we hold resumes from the next successful call. Messages sent during an outage are not stored by us.
report never throws
report runs in the background. If it fails, onError is told and the call is not retried; your agent is never interrupted. If a report is still in flight when the next decorate on the same memory starts, the SDK waits up to two seconds for it, so the next request sees the reply.
Errors from the HTTP API
Every error has one shape:
{ "error": { "code": "not_found", "message": "…", "hint": "Use an id that POST /v1/memories returned." } }| Code | Status | Meaning |
|---|---|---|
invalid_request | 400 | The body does not match the schema. |
unauthorized | 401 | The key is missing, revoked or not a project key. |
not_found | 404 | No memory with this id in this key's project. |
rate_limited | 429 | Too many requests for this key. |
quota_exceeded | 429 | The plan's allowance is used up. |
internal | 500 | Something went wrong on our side. |