Put any service in front of every model call and you have made it a dependency of every answer your agent gives. If it is slow, the agent is slow. If it is down, the agent is down. Memory must not be the reason an agent stops answering, so the Threadless AI SDKs are built to fail open.
The local tail
The SDK keeps a local tail for each memory, in your process: your latest instructions and the newest turns of the last request we returned, up to a size you set (60,000 estimated tokens by default), cut at a user message so a tool call is never separated from its output.
When decorate cannot get an answer from us, it returns a request built from that tail plus your new messages, and your model call goes ahead.
const memory = new Threadless({
timeoutMs: 15_000,
tailTokens: 60_000,
onError: (error, { operation, memoryId }) => log.warn({ error, operation, memoryId }),
});When it falls back
| Situation | What decorate does |
|---|---|
| Network error or timeout (15 s by default) | Falls back to the tail |
5xx from us | Falls back to the tail |
429 rate limited or quota used up, 408 | Falls back to the tail |
Any other 4xx: bad request, revoked key, unknown memory id | Throws, with a code and a hint |
Bugs are not hidden. A revoked key or a memory id from the wrong project is something you need to see, so those throw a ThreadlessError instead of quietly degrading.
Every fallback is reported to your onError callback with the operation and memory id, so you can alert on it.
What you lose during a fallback
The tail is a good request, not a complete one. While it is in use:
- the request is just the tail and the new messages: no facts are added, and no older messages are brought back;
- messages sent during the outage are not stored by us;
- a process that restarted has no tail, so its first fallback carries only your instructions and the new messages.
When we are reachable again, the next decorate resumes from the history we hold.
report never throws
report runs in the background and never throws. If it fails, onError is told and the call is not retried. 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.
Questions to ask any memory provider
- What does my agent do when your API times out?
- Which errors fail my request, and which degrade it?
- Is the fallback in my process, or another network call?
- What do I lose during an outage, and what comes back after?
Questions people ask
What happens to my agent if the memory service is down?
It keeps answering. decorate builds the request from the local tail of recent turns in your process plus your new messages, and your model call goes ahead.
Which errors does the SDK still throw?
Any 4xx other than 408 and 429: a malformed request, a revoked key or an unknown memory id. Those are bugs to see, so decorate throws a ThreadlessError with a code, a message and a hint.
How long does the SDK wait before falling back?
15 seconds by default. Set it with timeoutMs in TypeScript or timeout in Python.
Is anything lost during an outage?
Messages sent during the outage are not stored by us, and no facts are added and no older messages are brought back while the fallback is in use. The next successful decorate resumes from the history we hold.
Does the local tail survive a restart?
No. It lives in your process's memory only, so the first fallback after a restart carries just your instructions and the new messages. The SDK keeps tails for the 200 most recently used memories.
Does a quota error stop my agent?
No. 429 quota_exceeded is treated like an outage: decorate falls back to the local tail and reports the fallback to onError.