Empty artifact list after a completed turn
Two causes, and they look identical. The agent wrote somewhere that is not published. Only/workspace/outputs is
collected; anything else lives and dies with the workspace. Ask for the path
explicitly — Create /workspace/outputs/report.md — rather than hoping.
Or you looked too early. status: "completed" means the agent stopped working,
not that its files are fetchable. The turn carries a second field for that:
pending. The field is null while the turn is
still running, pending while its files are being collected, failed if that
collection did not work — and ready or partial when it has finished. An
empty list and a list that is not ready yet are the same shape, which is the
whole reason the field exists.
partial means publishing finished and left something behind. The turn
carries artifacts_skipped, naming each file and why:
ready, the file was simply absent, and the
reason lived only in our logs — so the obvious conclusion, that the agent wrote
to the wrong place, was the wrong one.
→ quickstart.md step 7
401 on every call with a new token
The token is almost certainly CLI import, which carries only thecli scope
and is refused by every /v1 route. That is the default in the Console, because
tokens minted before this API existed must keep doing exactly what they did.
Mint a new one and choose Agent API · read/write — or read if your
integration only watches.
project_limit_exceeded that retrying never clears
It never will. This is a ceiling on how many sessions an organization may hold
at once, not a rate limit, and waiting changes nothing. Delete a session you are
finished with:
rate_limit_exceeded arrive as 429. They are told apart by
error.code, and only the second is worth retrying. A client that treats every
429 the same will spin forever on the first.
If you are running many sessions in a loop, delete each one as you finish with
it rather than at the end. We filled our own ceiling this way and spent an
afternoon reading the resulting failures as product defects.
→ limits.md
Slow first request to a published URL
It was asleep. Publishing grants a stable address, not a permanently running computer: an idle workspace pauses on the usual schedule and the first public request wakes it. Your visitor waits a few seconds once. This is deliberate — a published preview is the agent’s work left reachable, not a hosting product — but it does mean the first hit after a quiet period is slow. → preview.mdInstructions that appear to be ignored
Read the session back. Ifagent.instructions is null, the field was not
accepted; if it holds your text, the instruction reached the session.
bridge_incompatible
The workspace is running an agent runtime older than the feature you asked for —
instructions, host functions, or session tools, depending on which you used.
Sessions created before a sandbox release keep the runtime they started with.
Create a new session; it gets the current one.
This is a refusal on purpose. The alternative — accepting the field and running
without it — is the failure mode this error exists to prevent.
→ errors.md
Transport errors from an HTTP client
If you are on Node’s built-infetch and the body is large, the client may
raise a transport error rather than hand you our response. We answer an
oversized body with 400 and a readable message; a client that is still
uploading when the answer arrives can lose it.
Check the same call with curl. If curl shows a clean 400, the refusal is
ours and correct, and what you are seeing is your client.
→ errors.md
Rejected limit
The maximum page size is 100. Asking for more is a 400 rather than a
silent clamp, because a caller asking for 500 has a paging bug and quietly
handing back 100 lets it ship.
The refusal has no data field. A client that reads body.data without
checking the status will see an empty collection and conclude the session
produced nothing — which is a mistake we made twice against our own API.
→ limits.md
Refused model or credential
Three different refusals, and the difference decides what you do next:
The middle one used to be reported as the last one, which sent people to connect
a model they already had.
→ model-credentials.md
conflict on POST /files: “the workspace is not ready yet”
PUT /tools restarts
the session’s bridge so it can pick up the new configuration, and for some
seconds afterwards the workspace cannot be written to. Retry; nothing was
written, so there is nothing to undo.
This used to answer internal_error, which means we broke — so nobody
retried a state that passes on its own.
If retrying does not help, the cause is probably not the workspace at all:
an MCP server configured with required: true that cannot be reached refuses
the session, and that answers invalid_request naming the server. See
tools.md.
→ errors.md
A session says failed and there are no turns
A run that could not start never opens a turn, so GET /turns is empty and
status is all GET /sessions/{id} will tell you. The reason is an item:
required: true MCP server that cannot be reached (see
tools.md) and a workspace that could not be provisioned.
→ sessions
A session stays working and never finishes
Read its required_actions. If one has type: "question" or "approval", the
agent is waiting on a person, and your integration cannot answer it —
those are resolved in the Console. An API caller polling for function_call
alone waits forever.
Answering one through this API is refused, naming the reason. It was worse
before: the answer was accepted, closed the action, and left the session
reporting working with an empty required_actions while the agent stayed
blocked — nothing anywhere said a human was needed.
If nothing in your product can answer a question, stop the agent asking:
The event stream prints nothing, but events are arriving
The streaming text frame carriespayload.delta. The settled message carries
payload.text. Reading text on an agent.text frame yields undefined,
which most renderers print as an empty string — so the stream looks silent
while the durable frames beside it arrive normally.
400 naming redacted when you send tools back
You read the tool configuration, changed something, and sent it back. The
read-back withholds MCP secrets and names what it withheld in redacted, which
is not a request field — so the round trip is refused rather than silently
replacing your credentials with nothing.
Re-send the secret itself, or build the body from your own source instead of
from the read.
→ tools.md
Anything else
Every error carries arequest_id:
internal_error is always worth reporting — that code means we failed,
not that you did.
→ errors.md