Skip to main content
This guide shows you how to recognise and handle every failure Coffee Chats can return. It covers the three shapes a failure arrives in, how to tell a retry hint from a real failure, and the mistakes that turn a recoverable failure into lost or duplicated work. The error codes page is the reference table behind it, and the result object defines every field named here.

Everything below arrives with HTTP status 200

All tool-level failures are returned inside a successful protocol response. Only the transport carries other statuses: 401 for a missing or rejected access credential, 413 for a body over 2 megabytes, and 429 for a rate limit. Branching on HTTP status will not tell you that a write conflicted or that a student declined.

The three shapes a failure arrives in

A read tool’s failure carries no code. Almost all of them carry this fixed text:
Protocol error envelope
The reason is withheld on purpose: the reason could itself describe data the connection was not allowed to see. A call to a tool whose permission was not granted arrives in exactly this shape, and so does a write that fails before it produces a result of its own.Two failures say more than that, so read the text before you decide what to do. A lookup of one record by its id, where the account has no such record, names the kind of record it did not find. Eight kinds of record answer that way, and the text tells you to call the matching list tool, which is the action to take. One of the eight is a posting, which no tool lists, so find it with jobs.search or ask the student which posting to read. A call to meetings.transcript.list for a meeting whose transcript Coffee Chats does not hold says that no transcript is available and why, so the action is to stop asking for that transcript. Failures that carry no code gives all three texts in full.When the text is the fixed one, there is nothing in the response to branch on. Report the failure to the student and stop.

What retryable is worth

Every error object carries a retryable boolean. It is false on every failure the connector maps today, except on OUTCOME_UNKNOWN, which copies the flag from the record it is reporting and can therefore arrive as true. In a retry hint, retryable is fixed at true by the contract. Do not branch on it alone. Use the Action column in the error codes tables, because several codes need a new idempotency key rather than a plain retry, and an uncertain outcome needs a read rather than either.

How long to keep retrying

The connector sets no retry ceiling and no backoff schedule. The only delay it states is retryAfterMs in a retry hint, which the contract bounds to between 1 and 5,000 milliseconds. The following is guidance, not a rule the connector enforces. Honour that delay. Cap the attempts at a small number. Stop once a result is completed, failed, cancelled or uncertain, because nothing moves out of those four. One connection may make 120 calls a minute, and a loop spends that budget on one change.

Three codes that read differently by tool

VERSION_CONFLICT, ACTION_DENIED and ACTION_EXPIRED have a different cause on a safe write, on a proposal and on a withdrawal, because those tools fail for different reasons. Each tool’s own page in the tool reference prints the wording that applies to it. VERSION_CONFLICT is the one worth spelling out. On a safe write it means either that the stored value moved under the agent, or that the same idempotency key was sent again with different input. On a proposal it means either that the booking page moved between the proposal and the approval, or, again, that the same key was sent again with different input. On actions.cancel it means neither of those: the review request you asked to withdraw was already decided, already expired or already being applied.

When a cancelled result is visible at all

After the student disconnects, the agent’s credential stops working, so most calls are refused before they reach a result. You will therefore rarely see OPERATION_CANCELLED from a disconnect. You see it only when the disconnect removed a different connection from the one the agent is using. Treat a refused call as the end of the connection. A cancelled result from actions.cancel is different and always visible, because the agent asked for it on a live connection.

Five things never to do

Never retry an uncertain outcome under a new key before reading. An uncertain outcome means Coffee Chats cannot tell whether the change landed. A new key is a new change, so retrying under one without reading first can apply the same change twice. Settle an uncertain result gives the read that settles each write.
Never build against a result shape you have not seen documented for a specific tool. The result format allows shapes that no tool returns today, including the requires_user_action tag. Use each tool’s reference page as the list of shapes it can return, and report anything else as an unrecognised answer instead of acting on it. Never infer a reason from the fixed read failure text. That text is fixed by design, and an agent that reads a missing permission or a temporary fault into it is guessing. Act only on what a failure says: the text that names a record your account does not have is telling you to call the matching list tool, and Tool call failed. is telling you nothing beyond that the call failed. The one exception is a posting, which no tool lists, so there ask the student which posting to read instead: failures that carry no code states that exception in full. Never keep calling a disconnected connection. Once the student disconnects, the protocol call is refused and renewing is refused too. Stop, and ask the student to authorize again if the work still matters. Never show a raw code to a student on its own. The codes are for the agent. Say what happened and what is stored now.

Rate limiting is not an error code

A call refused for rate limiting is answered with HTTP status 429 and these headers: RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and Retry-After. None of the published codes appears in that answer. The protocol limiter answers with this body:
Rate limit response body
Wait the number of seconds in Retry-After before calling again. A polling loop that ignores it will spend the whole budget on one waiting change and lock the agent out of its own reads. The limits page states each limit and what it counts.

Next steps

Error codes

Every code, its cause and the action to take.

Settle an uncertain result

The one failure that needs a read rather than a retry.