How to use this page
Each entry starts with what you see: an error code, a message or a behaviour. Then it gives the cause and the fix. Entries are grouped in the order you are likely to meet them, from setup to signup. Search the page for the exact code your agent reported. Every code is also listed in Error codes, with whether it is safe to retry.Setup and connection
The MCP server never starts
Cause. Almost always the package URL in the install command. A 404 or a DNS failure points to a mistyped URL. Fix. Check the install URL against the one on Install the SDK, or copy the finished command from the dashboard’s framework picker on Overview or API keys. On a self-hosted Agent Vault, always copy it from the dashboard: it uses your deployment’s URLs.My agent got Failed to connect to proxy at ...
Cause. The proxy is unreachable at SELF_AGENT_PAY_PROXY_URL.
Fix. Check the variable, including the scheme. Then check the proxy with
GET /health on its HTTP origin, which returns {"ok":true,...}.
My agent got invalid_api_key from /v1/tokens
Cause. The key is wrong or revoked. The API returns the same code for
both, so it cannot be used to test whether a key exists.
Fix. Check the key’s status on the API keys page before assuming it was
mistyped.
The hosted MCP server stops working and asks me to sign in again
Cause. The sign-in access token expired or was refused. The hosted server answers HTTP 401 witherror="invalid_token" before any tool runs, so the agent
gets no tool error.
Fix. Sign in again from your MCP client, the same way as the first time.
Retrying the tool does not help. If the next call answers that you must choose
what the app may use, choose access for the new row on Connected apps. See
Sign-in responses.
My agent got unavailable when connecting
Cause. The proxy instance it reached is shutting down. Nothing was started
and nothing was spent.
Fix. Open a new connection rather than retrying on the same one. If it
repeats, an update is rolling out: wait a little and connect again.
On the hosted MCP server, a tool error reading “Agent Vault could not be
reached. Retry in a moment.” is a different unavailable: the server could not
get a credential for the call. Nothing ran. Retry the same tool in a few
seconds, and keep the connection and your sign-in. See
Tool errors.
My agent got rate_limited from listTokens()
Cause. The key spent its token catalog budget for the current minute.
Fix. Wait for retryAfterSeconds (from the Retry-After header), then
retry. If the value is missing, wait a few seconds anyway: a proxy may have
stripped the header. Fetch the catalog once per task and reuse it, since tokens
do not change. Reuse a full catalog from listTokens() without url: one
narrowed to a site holds only that site’s cards and logins, so fetch again for a
different host.
Tokens
My agent got token_not_found: no matching record on file
Cause. The token parses but matches nothing. One of:
- The record was deleted.
- The account holder removed the login field it stood for.
- The label is wrong, or the account holder renamed the address it named.
- A token of the wrong kind was used.
- An identity token names an empty field.
scope_denied instead.
Fix. Fetch a fresh catalog and use its tokens. Two cases return the same
token on every refresh:
- The vault cannot fill the card. The account holder removes and re-adds it.
- An identity field is empty and listed in
missing. The account holder fills it in on their profile. Retry once it leavesmissing: identity tokens never change.
A totp token returns token_not_found
Cause. That login has no stored 2FA secret, or the account holder removed it.
Fix. Fetch a fresh catalog, and fill a totp token only for a login showing
hasTotp: true. If the site asks for a code anyway, it belongs to the account
holder: hand it off.
My agent got cross_user_denied, then key_auto_revoked
Cause. A token belongs to a different account than the API key. Three of
these within five minutes revoke the key automatically.
Fix. Stop and check where the tokens came from. Never retry with the same
token. After a revocation, the account holder creates a new key on the API
keys page.
A phone fill is rejected or cut short
Cause.fill_rejected, because the field’s maxlength is shorter than the
whole number. A form with a country selector next to a number box refuses the
full-number token every time.
Fix. Fill tokens.phone_country into the country field and
tokens.phone_national into the number field. Both resolve for any country.
For a three-box US form, use phone_area, phone_prefix and phone_line. For a
number outside +1 those three return token_not_found, which means use
phone_national, not that the number is unusable.
My agent got merchant_blocked on a card marked anySite
Cause. The agent card on this card has a block list, and this host is on it.
anySite only means the card is not limited to an allow list. The block list is
private to the account holder.
Fix. Do not retry the same merchant. Tell the account holder the card is not
permitted there, or use a different card.
My agent got card_blocked
Cause. The card number is ambiguous: it matches several differently
configured cards. Or the revealed card failed its binding checks. The result is
the same on every attempt.
Fix. For an ambiguous number, use a different card, or ask the account holder
to remove the duplicate. For a failed binding check, nothing was typed: check
Activity for a recorded transaction, then ask the account
holder to delete and re-add the card.
Sign-in
My agent got login_blocked
Cause. Either the page is not the login’s registered site, or the login has
no website set at all. The code does not say which.
Fix. If the login has never filled anywhere, assume the second: set its
website on the Logins page. Otherwise, navigate to the registered host or one
of its extraWebsites.
Filling both fields does nothing on an email-first sign-in
Cause. The password field is not on the page yet. Microsoft, Okta and Google all show the email step alone. Fix. Fill the username and submit it with Enter, because some of these pages ignore clicks on the button. Wait forinput[type="password"] to be
visible, not merely present: many keep a hidden password input in the page
the whole time. Then fill the password.
The login submitted and nothing obvious happened
Cause. Usually a step your agent did not recognise, not wrong credentials.detectChallenge() returning { detected: false } means “matched no known
pattern”. A real 2FA prompt with an unusual field name reads as clean.
Fix. Wait for either a signed-in marker or the error element, whichever comes
first. Look for a code field directly, and confirm it sits next to sign-in text.
Let a click prove whether a control is usable. See
Sign in to a site.
A field takes the value but its button stays disabled
Cause.fill() inserts the value in one step. It raises input but no
keydown, keyup or change. A field that filters digits on key press, or
enables its button on key release, never notices.
Fix. Use pressSequentially(). For a form that enables only on change, add
press("Tab") after the fill.
Checkout
There is no add-to-cart button, and count() returns 0
Cause. Often a region or delivery gate, not a wrong selector or an
out-of-stock item. Large retailers hide the buy button, and sometimes the price
and stock, until a delivery address is chosen. The proxy’s browser starts with
an empty profile every run, so the gate appears every time.
Fix. Fill the location box from the address tokens, which resolve on any
authorized destination, then check again. The registry records a known gate as
preconditions: ["delivery_location"].
A click times out because another element intercepts pointer events
Cause. The target is there, but something is on top of it. Retrying will not help. Fix.press(selector, "Enter") skips the hit test, so it works on anything a
key activates: a button, a link with an href, or a submit, button or image
input. A checkbox or radio takes Space. Inside a frame, use that frame’s own
press.
A clickable div or a label ignores keys: clear the overlay instead. If a press
succeeds but nothing moves, a modal may be blocking the page: clear it.
My agent got limit_reached on a purchase well under the limit
Cause. A spending limit on the agent card stopped the purchase. An
unreadable total is checkout_total_unknown instead. Likely causes:
- The agent card auto-paused after an earlier breach.
- An earlier card number fill already counted toward a window limit. A fill counts the moment it lands, even with no order.
- The agent filled the security code on its own. On a card with a spending limit that is always refused.
- On an older deployment, a plan quota (now
plan_quota_reached).
My agent got checkout_total_unknown
Cause. The order total could not be established, so nothing was compared
with any limit. No limit was exceeded.
Fix. Do not send the account holder to raise a limit: it changes nothing. The message
names one of three causes. Only “reading the page failed” is worth another
attempt. “A figure was found and refused” and “no order total was found in the
page’s text” fail the same way on a retry. A page hint can help with the last.
My agent got plan_quota_reached
Cause. The account’s plan quota, not a card setting. Either the plan’s
payments for the rolling 30 days are used up, or the agent card is beyond the
plan’s agent card limit.
Fix. Payment quota: payments clear as the oldest age out of the 30-day window. On Free
or Plus, upgrading on Billing raises the allowance. Enterprise has no payment
quota.
Agent card quota: this never clears by waiting. Slots go to the oldest agent
cards linked to an existing funding card. Deleting a newer card, or one showing
No funding card, changes nothing. Upgrade, or remove older linked cards. See Limits.
The account holder was asked to approve the same purchase twice
Cause. Something ran between the card number and the security code. Filling the number authorizes exactly one security code fill, and the next action on the page uses that authorization up: another field in the same batch, a click, or a wait that returns. Screenshots,observe(), detectChallenge() and count()
do not, unless a screenshot returns securityCodeAuthorizationReleased.
Fix. Fill expiry and billing first, then the number, then the security code
immediately after. See Run a checkout end to end.
My agent got cvv_entry_required
Cause. The security code is never stored. Either the account holder did not
enter it live in time, or this deployment does not offer live entry.
Fix. If the account holder missed the prompt, or it could not be started,
report it and let them retry when they can respond.
A retry fills the card number again. An earlier number fill is not reversed and
counts toward the card’s limits: check Activity first. See
Error codes.
If the message says “Live CVV entry is not yet available”, no retry helps: stop
and tell the account holder this deployment cannot complete a checkout that
asks for a security code. Never ask for or type a real security code. See
Live CVV entry.
A card iframe is visible but no selector finds the fields
Cause. The fields are inside a payment iframe.listFrames() is refused with
sensitive_data_blocked once any token has been filled, which by the card step
it has.
Fix. Target the frame by CSS: iframe[title="Secure payment input frame"] for
the current Stripe Payment Element, iframe[title="Secure card number input frame"]
for the older split fields. Input names vary, so probe each with count()
before filling the number. If nothing matches, stop and report it rather than
submitting blind.
Reading the page
Every screenshot is black
Cause. Working as designed. After the first token fill, every screenshot for the rest of the session is blacked out. A page can echo a filled value anywhere, and pixels cannot be cleaned. Fix. Read theredaction field on the result: it says blackout. Use
observe(), count() and detectChallenge() instead, and take any screenshots
you need before filling. A black image is not a dark page and not proof a
click did nothing.
My agent got a withheld screenshot
Cause. Different from a blackout. The image could not be measured safely, so it was refused. Fix. Checkredaction to tell the two apart, and read the page with
observe() instead.
My agent got sensitive_data_blocked from snapshot_page or list_frames
Cause. After the first token fill, both are refused, because accessible names
and frame titles are text the page controls.
Fix. Inspect the page before filling anything. A new session starts clean, so
if you need frame details late, open one and inspect before filling.
observe() keeps returning false for something visible
Cause. The scan did not finish. Look for inconclusive: true: every false
beside it means unknown, not absent.
Fix. Test !result.inconclusive. A finished scan leaves the flag out rather
than setting it to false, so waiting for a literal false waits forever.
A snapshot was truncated
Cause. The page was larger than one result. Fix. The last line names a cursor and a depth. Pass both. Do not lower the depth on its own: the cursor belongs to that depth. Page with the cursor when the page is wide, such as a long product list. Lower the depth and drop the cursor when it is deeply nested.Sessions
The session closed with code 4008 or 4009
Cause.idle_timeout after 15 minutes with no message and no call in
flight, or hard_timeout after 60 minutes in total. A long call keeps the idle
timer from firing, but nothing resets the hard timer, so a call still running at
the 60 minute mark can be cut off after it reached the merchant.
Fix. Launch a new browser. Tokens do not expire. Before repeating anything
that changes state, such as placing an order, check whether it already happened.
My agent got unanswered and relaunched the browser
Cause. unanswered means the proxy stopped waiting for one call. The
connection, the browser and its cookies are intact. Relaunching threw away a live
session.
Fix. For a read, retry once. For an action, close the tab first, since the
abandoned action can still land until the tab is gone. Then check the merchant’s
state and continue in a new tab of the same context.
My agent got request_timeout on every call after one slow one
Cause. The SDK’s own deadline closed the connection.
Fix. Set SELF_AGENT_PAY_REQUEST_TIMEOUT_MS to 150000 or above, or leave
it unset for the 180000 default. The proxy answers a stalled call at 120 seconds
with the recoverable unanswered, and a shorter client deadline closes the
session first. A call with its own timeout needs nothing extra.
My agent got insecure_site opening a page
Cause. The session goes out through the deployment’s residential egress,
which is HTTPS only. The site would not load over HTTPS, or it redirected to an
http:// address. Nothing was sent over plain http, and the tab did not move.
Fix. Do not retry the same address: it fails the same way every time. Use
another site, or tell the account holder this one cannot be used. See
Residential egress.
Signup
My agent got signup_not_enabled
Cause. This API key does not have the signup permission. It is off by default.
Fix. Ask the account holder to turn it on for this key on the API keys
page. See Agent signup.
A signup request has been pending far longer than expected
Cause. Approval may be failing on the account holder’s side, with no code sent to the agent. One of:- The key’s list of allowed logins is full.
- The site’s name has no free numbered labels left.
- The account reached 500 logins while the request waited. Every login counts.
- The daily budget for agent-made credential changes is spent.
login_limit
instead, and no request is created.
Fix. Tell the account holder to check their dashboard. They need to free the resource
and approve again.
My agent got too_many_pending with no Retry-After
Cause. Five signup requests are already waiting for the account holder. There
is no Retry-After by design, because it would reveal when the oldest request
was filed.
Fix. Do not poll tightly. Tell the account holder requests are waiting for
their decision. It clears as requests are decided or expire after 24 hours.
My agent got write_outcome_unknown from createLogin()
Cause. The request may or may not have been filed.
Fix. Do not retry. A second request is a second approval prompt and uses a
second of the five pending slots. Tell the account holder: their dashboard lists
every waiting request, and an approved one appears as a login in listTokens().
Registry and webhooks
My agent got rate_limited from submitRecipe()
Cause. The account’s recipe submission budget is spent. It is a rolling
24 hours, so there is no Retry-After: each submission counts until it is 24
hours old.
Fix. Stop submitting until the oldest submission in the last 24 hours turns
24 hours old. Do not confuse it with the catalog’s
rate_limited, which clears in seconds: tell them apart by which call threw.
My agent got invalid_recipe
Cause. The body was not JSON, was over 64 KB, or failed validation.
Fix. Read validationErrors on the thrown error, or errors in the HTTP
response. Fix each problem and submit again. The same body always fails the same
way. See Recipe format.
My webhook signature never matches
Cause. Usually the body was parsed and re-serialised before verifying, or the wrong card’s secret is used. Fix. Verify against the raw request bytes, with the signing secret of the agent card named inagent_card_id. Sign t + . + body. See
Verify a delivery.
My webhook never arrives
Cause. The account is on Free, the URL is not public HTTPS, the endpoint did not answer2xx in time (10 seconds with no data, or 15 seconds in total), or
it returned a redirect.
Fix. Click Send test on the agent card and read Recent deliveries,
which shows each attempt’s status code. See Webhooks.
Still stuck
Error codes
Every code, with whether to retry.
Support
Include the session id from the Activity page.