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.

/docs/failure-behaviour.md

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 5xx response;
  • 429 (rate limited, or the plan's quota used up) and 408.

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.

TypeScript
const memory = new Threadless({
  timeoutMs: 15_000,
  tailTokens: 60_000,
  onError: (error, { operation, memoryId }) => log.warn({ error, operation, memoryId }),
});
Python
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:

JSON
{ "error": { "code": "not_found", "message": "…", "hint": "Use an id that POST /v1/memories returned." } }
CodeStatusMeaning
invalid_request400The body does not match the schema.
unauthorized401The key is missing, revoked or not a project key.
not_found404No memory with this id in this key's project.
rate_limited429Too many requests for this key.
quota_exceeded429The plan's allowance is used up.
internal500Something went wrong on our side.