intent_invalid
The agent’s mode is optional or required, a Deedglass-Intent header was sent, and one of checks 1 to 12 failed §6 has the order).
HTTP 403 · permission_error · not retryable
What happened
Section titled “What happened”The agent’s mode is optional or required, a Deedglass-Intent header was
sent, and one of checks 1 to 12 failed §6 has the order). The
logged reason says which. POST /v1/intents/verify checks the header in every mode, off
included. It is 403, never 401.
| Reason | Param | Message |
|---|---|---|
syntax |
Deedglass-Intent |
The Deedglass-Intent header must be two base64url parts joined by a dot: the intent, then its signature. |
too_large |
Deedglass-Intent |
The signed intent is larger than 2,048 bytes. Keep only the fields listed at docs.deedglass.com/intents. |
strict |
Deedglass-Intent |
The signed intent isn't strict JSON: {detail}. Use the SDK signer, or follow the profile at docs.deedglass.com/intents. |
not_canonical |
Deedglass-Intent |
The signed intent isn't canonical JSON (RFC 8785): keys must be sorted and there must be no whitespace. Use the SDK signer, or canonicalize before signing. |
schema (unknown field) |
the field | The signed intent has an unknown field "{field}". Put arguments in the call, and their digest in args_digest. |
schema (missing field) |
the field | The signed intent is missing the field "{field}". Sign every required field listed at docs.deedglass.com/intents. |
schema (wrong type or pattern) |
the field | The signed intent's {field} isn't valid: it must be {want}. See the field table at docs.deedglass.com/intents. |
version |
v |
The signed intent's version is "{got}", and Deedglass reads deedglass.intent/1. Sign with deedglass.intent/1. |
org_mismatch |
org |
The signed intent names {intent_org}, but this API key belongs to {org}. Sign with the agent's own org id. |
agent_mismatch |
agent |
The signed intent names {intent_agent}, but this API key belongs to {agent} ({agent_id}). Sign with the agent's own identity. |
unknown_kid |
kid |
The signed intent uses key {kid}, which isn't registered for {agent}. Register it with deedglass agent intent add {agent}, or in Console › Agents › Security. |
bad_signature |
kid |
The signature doesn't verify with key {kid}. Sign the bytes "deedglass/intent/v1\n" + intent_hash with the matching private key. |
not_yet_valid |
iat |
The signed intent was issued {ahead} in the future. Check the agent's clock; Deedglass allows 60 seconds of skew. |
expired |
exp |
The signed intent expired at {time} UTC, {ago} ago. Sign a fresh intent for each call; an intent lasts at most 5 minutes. |
ttl |
exp |
The signed intent is valid for {ms} ms, and it must be more than 0 and at most 300000 (5 minutes). Sign it again with exp after iat, at most 5 minutes later. |
tool_mismatch |
tool |
The signed intent is for {intent_tool}, but this call is for {tool}. Sign one intent per call. |
big_int |
args |
The arguments contain an integer larger than 9007199254740991, which JSON can't carry exactly. Send it as a string. |
args_not_canonicalizable |
args |
The arguments can't be canonicalized with RFC 8785: {detail}. Send JSON with unique keys, at most 32 levels deep and at most 256 KiB. |
args_mismatch |
args_digest |
The signed intent's args_digest doesn't match the arguments sent. Sign the exact arguments you send, canonicalized with RFC 8785. |
unsigned_obo |
Deedglass-On-Behalf-Of or Deedglass-Session |
{name} was sent, but the signed intent doesn't include {field}. Sign it too, or leave the header out. |
obo_mismatch |
Deedglass-On-Behalf-Of |
Deedglass-On-Behalf-Of says {header}, but the signed intent says {intent}. When you sign an intent, send the same value in both, or only in the intent. |
session_mismatch |
Deedglass-Session |
Deedglass-Session says {header}, but the signed intent says {intent}. When you sign an intent, send the same value in both, or only in the intent. |
{placeholders} are filled at the call site. INTENTS.md §6 lists every {detail}
and {want}. A message never quotes the header itself.
What to do
Section titled “What to do”Read the message: it names the field and the fix. Most refusals come from signing
something other than what you send. Use the SDK signer, sign the exact tool and arguments of this
call, sign a fresh intent for every call with a clock within 60 seconds of the server’s, and put
on_behalf_of and session in the intent rather than only in headers. To check a signer without
acting, send its header to POST /v1/intents/verify.
Example
Section titled “Example”{ "error": { "type": "permission_error", "code": "intent_invalid", "message": "The signed intent's args_digest doesn't match the arguments sent. Sign the exact arguments you send, canonicalized with RFC 8785.", "param": "args_digest", "request_id": "req_01j9zr3m5k8q2w7x4t6v0b1c9d", "doc_url": "https://docs.deedglass.com/errors/intent_invalid" }}The message is for a person and can get more exact over time: branch on type and code, never on the message.
In the SDKs
Section titled “In the SDKs”| SDK | Raised as |
|---|---|
TypeScript (@deedglass/sdk) |
PermissionDeniedError with code === "intent_invalid" |
Python (deedglass) |
deedglass.PermissionDeniedError with .code == "intent_invalid" |