Reference / Errors
Errors
Every error the guard returns carries a code you can branch on. Find yours in the lists below, or link to it with /docs/errors#code.
On a proxy error, read guard-status first. guard-status: not_forwarded means this request did not leave the proxy. It does not say whether an earlier send paid, so follow the entry for the code before you re-sign.
Never resend a payment that answered duplicate_payment or decision_already_used. Check the decision and your wallet first. decision_already_used means it paid. After duplicate_payment, sign a new payment only once this one has expired and no transfer shows.
The error envelope
The API and the proxy URL answer an error the same way. The body holds a code and a message that names the cause and the next step.
HTTP/1.1 409 Conflict
guard-status: not_forwarded
guard-decision-id: 7c0e1f52-3b8a-4d2e-9a61-0f4c2d8b5e17
{
"error": {
"code": "decision_mode_changed",
"message": "This payment was checked in observe mode before enforcement was enabled. Nothing was forwarded. Request a fresh quote and sign a new payment under the agent's current mode."
}
}A proxy error that belongs to a decision also carries guard-decision-id. The SDK throws each API error as a GuardApiError with its code and status. When the SDK hook blocks a payment, it throws a plain Error with no code, and its message gives the reason. The SDK page shows how to handle it.
The MCP tool is advisory. The agent chooses whether to ask. For a check it cannot skip, use the proxy URL or the SDK hook. The advisory MCP server calls the same API, and most of its tool errors name the guard's code and status.
Proxy URL codes
Only the proxy URL returns these.
| Code | HTTP | Lane | Cause | Next step |
|---|---|---|---|---|
bad_ | 502 | Proxy URL | The seller's 402 carries no x402 v2 payment terms the proxy can read. | Ask the seller for an x402 v2 quote. |
bad_ | 400 | Proxy URL | The seller URL after the token is missing or malformed. Or it is not https, carries a username or password, names a port, points at a private address, runs over 2,000 characters, or sits on this site outside /merchant/. | Put a public https seller URL after the token, without its scheme. Pay a seller on your own machine through the SDK. |
bad_ | 400 | Proxy URL | x-vulsight-require-mode carries a value other than enforce. | Send enforce, or leave the header out. |
content_ | 503 | Proxy URL | The guard could not save the check of an answer the agent read from this seller, so the payment was not forwarded. | Send the payment again shortly. |
content_ | 503 | Proxy URL | A page or 402 the agent read from this seller is still being checked, so the payment was not forwarded. | Send the payment again after the seconds in retry-after. |
decision_ | 503 | Proxy URL | The proxy decided the payment but could not reach its database before forwarding it. | Send the payment again after retry-after. If that answer carries a new guard-decision-id, follow that one. If it answers duplicate_payment, follow that entry. |
duplicate_ | 409 | Proxy URL | The proxy already decided this signed payment, and another send of it may have reached the seller. | Do not send it again. Check your wallet and the decision in guard-decision-id. Sign a new payment only once this one has expired and no transfer shows. If the answer has no guard-decision-id, a newer signed payment took over; follow that payment's answer. |
key_ | 400 | Proxy URL | The request carries a VulSight API key or proxy token in Authorization. | Remove it. The token in the URL is all the proxy needs, and the seller must not see your key. |
legacy_ | 409 | Proxy URL | A decision saved by an older release does not name the exact seller offer. | Wait for that decision to expire, then start a new payment. |
legacy_ | 409 | Proxy URL | A review saved by an older release has no verified payer. | Check that decision and your wallet. Wait for it to expire before you start a new payment. |
merchant_ | 502 | Proxy URL | The seller stopped part way through its answer, so none of it was passed on. | Read the message before you ask again. On a paid request it says whether the payment settled. |
merchant_ | 502 | Proxy URL | The seller answered in a form the proxy cannot pass on, such as a status outside 200 to 599. | Read the message before you ask again. On a paid request it says whether the payment settled. |
merchant_ | 502 | Proxy URL | The seller's answer is larger than the 4 MiB the proxy relays. | Ask the seller for a smaller resource. On a paid request, the message says whether the payment settled. |
merchant_ | 504 | Proxy URL | The seller sent its headers, but its body did not finish in the time the proxy waits. | Read the message before you ask again. On a paid request it says whether the payment settled. |
merchant_ | 502, 504 | Proxy URL | The seller could not be reached (502), did not answer in time (504), or the proxy had too little time left to forward the payment. | Check the URL and try again. With guard-status: not_forwarded, re-sign and send it again. With a guard-decision-id only, read that decision before you pay again. |
no_ | 400 | Proxy URL | The proxy has no 402 on file for this URL. | Request the URL through the proxy first, then send the payment. If the seller redirects before it asks for payment, request it with redirect: "manual" and pay theLocation. |
payment_ | 403 | Proxy URL | The policy denied the payment, or its review was denied or expired. guard-status says which. | Read the reasons in the message and on the decision in decisionId. The reason codes say what each one means. |
redirected_ | 409 | Proxy URL | The first payment reached the seller, which redirected to a URL that asks for payment again. No second payment was sent. | Check the first payment's decision on Decisions. Then pay the proxy URL the message names if you still need it. |
relay_ | 429 | Proxy URL | This answer would take the account past the 256 MiB of seller answers the proxy relays in a UTC day. No payment was forwarded. | Try again after midnight UTC. retry-after counts down to it. |
resign_ | 402, 409 | Proxy URL | The signed payment expired, or came too close to expiring, before the proxy could forward it. | Request the URL again for a fresh quote and sign that. guard-next-step reads fresh_quote. |
review_ | 402 | Proxy URL | The payment is held for a person, and the review was still open when the proxy answered. | Approve it on Review, then send the payment again after retry-after, signed again if it has expired. The same review answers it. |
revoked_ | 401 | Proxy URL | The proxy token was revoked. | Create a new key on the Keys page and put its proxy token in the URL. |
sign_ | 503 | Proxy URL | The URL carries token, and the proxy could not check whether it is a password reset code for this site. | Try again in a few seconds. |
sign_ | 400 | Proxy URL | The link carries a sign-in or password reset code for this site. | Do not use the link again. Sign in or reset your password on this site. An agent calls the proxy URL from a fetch client, not a browser page load. |
solana_ | 503 | Proxy URL | The proxy could not check the Solana blockhash before forwarding. | Request a fresh quote and try again in a minute. |
unknown_ | 401 | Proxy URL | The token is not recognized or was rotated. An API key (vs_test_) in the URL gets this code too. | Put the proxy token (vsp_test_) from the Keys page in the URL. If an API key was in a URL, rotate it on Keys. |
unreadable_ | 400 | Proxy URL | The proxy cannot read or pay the PAYMENT-SIGNATURE header. The message names the cause. | Have the client pick a supported payment from the seller's 402, or ask the seller for one. |
x402_ | 400 | Proxy URL | The payment came in an X-PAYMENT header. The proxy URL takes x402 v2 only. | Send the payment in PAYMENT-SIGNATURE. |
HTTP API codes
The HTTP API is advisory. Your code acts on its answer.
| Code | HTTP | Lane | Cause | Next step |
|---|---|---|---|---|
admin_ | 403 | HTTP API | The content history routes are for administrators only. | View your content checks in the dashboard. |
idempotency_ | 409 | HTTP API | This Idempotency-Key was already used for a different payment. | Send a new key for a new payment, or the same body to read that decision again. |
invalid_ | 422 | HTTP API | The before and beforeId of a content history call do not form a page cursor. | Send both values from the previous page's next. |
invalid_ | 422 | HTTP API | The body, the query, x-vulsight-channel or Idempotency-Key is wrong. The message names the first bad field. | Fix that field and send the request again. |
method_ | 405 | HTTP API | The route does not take that method. | Send it again with a method the allow header names. |
missing_ | 401 | HTTP API | No Authorization header was sent, or its value is empty, such as Bearer undefined or Bearer null. | Send the key as a Bearer token, and check that the variable holding it is set. |
not_ | 404 | HTTP API | No decision, or content request, with that id belongs to your account. | Check the id, and that the key belongs to the account that made it. |
proxy_ | 403 | HTTP API | The proxy token in x-vulsight-proxy-token and the API key do not belong to the same active agent. | Send the key and proxy token the Keys page showed together for one agent. |
revoked_ | 401 | HTTP API | The key was revoked. | Create a new key on the Keys page. |
settlement_ | 409 | HTTP API | Another decision in your account already carries this transaction in this direction. The message names it. | Report the payment on that decision, or send the hash of the transfer that settled this one. |
settlement_ | 409 | HTTP API | The decision already holds eight settlements in this direction. | Make a decision for the payment this transfer settled, and report it there. |
settlement_ | 409 | HTTP API | The decision approved no payment, or the report's payee, amount, asset, network or payer differ from it. The message names the field. | Report the payment as the decision approved it, on the decision that allowed or approved it. |
unknown_ | 401 | HTTP API | The key is mistyped or was rotated, or a proxy token (vsp_test_) was sent as the key. | Check it for a stray character. Send the API key (vs_test_) the Keys page showed when it was made or rotated. |
unknown_ | 404 | HTTP API | No API route answers at that path. The message lists the routes. | Check the path. The API lives under /api/v1. |
Codes both return
The HTTP API and the proxy URL both return these.
| Code | HTTP | Lane | Cause | Next step |
|---|---|---|---|---|
bad_ | 400 | HTTP API and proxy URL | On the API, /api/v1/status or /api/healthz was called with a query string or a variant of its path. On the proxy URL, the request carries something that cannot be sent as written. The message names it. | Call the exact path with no query. On the proxy URL, remove what the message names and send it again. |
content_ | 503 | HTTP API and proxy URL | The guard could not save text for checking. On the proxy URL the seller may already have answered, and when an answer was held back the message says so. | Send it again. If the request changes something on the seller, check whether it took effect before you send it again. |
decision_ | 409 | HTTP API and proxy URL | This decision already paid. Its settlement is on file. | Do not pay for it again. To buy again, use a new Idempotency-Key on the API, or request a fresh quote and sign a new payment on the proxy URL. |
decision_ | 409 | HTTP API and proxy URL | The decision was made in observe mode, and the agent now enforces its policy. It would have been denied or held. | Start a new payment attempt. Use a new Idempotency-Key on the API, or a fresh quote and a new signature on the proxy URL. |
decision_ | 409 | HTTP API and proxy URL | The guard has no record that this decision finished its budget check, so it cannot be reused. | Start a new payment attempt. On the proxy URL, request a fresh quote and sign again. |
internal | 500 | HTTP API and proxy URL | Something failed on the guard's side. On the proxy URL, a paid request may or may not have reached the seller. | After a paid request, read the agent's latest decision on Decisions before you send it again. Otherwise try again. |
payment_ | 503, 504 | HTTP API and proxy URL | Wallet screening could not finish (504 when the proxy ran out of time). The message starts with the cause when it is known. | Send it again once screening recovers, after the retry-after seconds when the answer has them. If the message says to sign a new payment, do that. |
proxy_ | 409 | HTTP API and proxy URL | The agent is in observe mode, and preflight or x-vulsight-require-mode: enforce asked for enforce. | Switch the agent to enforce in the Mode card on Policy. On the proxy URL you can instead leave x-vulsight-require-mode out. |
rate_ | 429 | HTTP API and proxy URL | Too many calls this minute, or the account has sent its 16 MiB of text for checking this UTC day. The message names the limit. | Wait the seconds in retry-after, then send it again. The limits are in the rate limit table. |
request_ | 408 | HTTP API and proxy URL | The request body did not finish arriving in time. | Send a smaller body or use a faster connection, then send it again. |
request_ | 413 | HTTP API and proxy URL | The body is over 1 MiB on the API, or over the 4 MiB the proxy relays. | Send a smaller body. |
SDK codes
The SDK throws these itself when the guard's base URL does not answer as the guard.
| Code | HTTP | Lane | Cause | Next step |
|---|---|---|---|---|
http_ | Any | SDK | The base URL answered without the guard's error envelope. | Check baseUrl or VULSIGHT_BASE_URL. On a 408, 429 or 5xx, try again in a moment. |
redirected | 3xx | SDK | The guard's base URL answered with a redirect, and the SDK does not carry the key through one. | Set baseUrl or VULSIGHT_BASE_URL to the address the message names. |