{"record":{"id":"7bbbf22a6b1cd9a5","repo":"mastra-ai/mastra","slug":"failed-to-canonicalize-a2a-agent-card-for-signing","errorCode":null,"errorMessage":"Failed to canonicalize A2A Agent Card for signing","messagePattern":"Failed to canonicalize A2A Agent Card for signing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/server/src/server/a2a/agent-card-signing.ts","lineNumber":84,"sourceCode":"\nfunction getDigestAlgorithm(algorithm: string): string {\n  if (algorithm.endsWith('256')) return 'sha256';\n  if (algorithm.endsWith('384')) return 'sha384';\n  if (algorithm.endsWith('512')) return 'sha512';\n  throw new Error(`Unsupported JWS algorithm for A2A Agent Card signing: ${algorithm}`);\n}\n\nexport async function signAgentCard({\n  agentCard,\n  signing,\n}: {\n  agentCard: AgentCard;\n  signing: A2AAgentCardSigningConfig;\n}): Promise<AgentCard> {\n  const canonicalPayload = canonicalize(stripAgentCardSignatures(agentCard));\n\n  if (!canonicalPayload) {\n    throw new Error('Failed to canonicalize A2A Agent Card for signing');\n  }\n\n  const key = importSigningKey(signing);\n  const protectedHeader = getProtectedHeader(signing);\n  const encodedHeader = Buffer.from(JSON.stringify(protectedHeader), 'utf8').toString('base64url');\n  const encodedPayload = Buffer.from(canonicalPayload, 'utf8').toString('base64url');\n  const signingInput = `${encodedHeader}.${encodedPayload}`;\n  const signatureBuffer = crypto.sign(\n    getDigestAlgorithm(String(protectedHeader.alg)),\n    Buffer.from(signingInput, 'utf8'),\n    {\n      key,\n      ...getSignatureOptions(String(protectedHeader.alg)),\n    },\n  );\n  const signatureValue = signatureBuffer.toString('base64url');\n\n  if (!encodedHeader || !signatureValue) {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/a2a/agent-card-signing.ts#L66-L102","documentation":"signAgentCard() serializes the agent card deterministically using the canonicalize() (RFC 8785 JSON canonicalization) library after stripping existing signatures. If canonicalize returns a falsy value — which happens for input that is not valid canonicalizable JSON (e.g. undefined/invalid values, non-JSON-safe data like undefined fields, NaN, or a non-object) — the function throws this error rather than signing non-canonical data.","triggerScenarios":"Calling signAgentCard with an AgentCard whose serialized form is not RFC 8785 canonicalizable — e.g. the card is undefined/null at runtime, or contains values JSON can't represent deterministically.","commonSituations":"Agent card constructed at runtime with undefined fields (bigints, undefined, functions in values); a storage/registry layer returning an empty or malformed card; version drift where the AgentCard type gained non-JSON-safe fields.","solutions":["Log/inspect the agent card passed to signAgentCard and remove non-JSON-safe values (undefined, bigint, NaN, functions).","Ensure the card is a plain JSON object — deep-clone through JSON.parse(JSON.stringify(card)) before signing if needed.","Verify the card is fully populated (not undefined) before calling signAgentCard.","Check the canonicalize dependency is correctly installed/Functioning (a broken import could yield a non-function whose result is falsy)."],"exampleFix":"// before\nawait signAgentCard({ agentCard: maybeUndefinedCard, signing });\n// after\nif (!agentCard || typeof agentCard !== 'object') throw new Error('Agent card missing');\nconst safeCard = JSON.parse(JSON.stringify(agentCard));\nawait signAgentCard({ agentCard: safeCard, signing });","handlingStrategy":"validation","validationCode":"function isJsonSafe(v: unknown, seen = new Set()): boolean {\n  if (v === null || ['string','number','boolean'].includes(typeof v)) return true;\n  if (typeof v !== 'object' || seen.has(v)) return false;\n  seen.add(v);\n  return Object.values(v).every(x => isJsonSafe(x, seen));\n}\n// require isJsonSafe(agentCard) before signAgentCard","typeGuard":"const isAgentCardObject = (c: unknown): c is Record<string, unknown> =>\n  typeof c === 'object' && c !== null && !Array.isArray(c);","tryCatchPattern":"let signedCard;\ntry {\n  signedCard = await signAgentCard({ agentCard, signing });\n} catch (e) {\n  if (e.message.includes('Failed to canonicalize')) {\n    const safe = JSON.parse(JSON.stringify(agentCard));\n    signedCard = await signAgentCard({ agentCard: safe, signing });\n  } else throw e;\n}","preventionTips":["Round-trip the agent card through JSON.parse(JSON.stringify(...)) before signing.","Keep bigint/undefined/NaN values out of AgentCard fields.","Assert the card is loaded and non-null before signing."],"tags":["a2a","jws","canonicalization","crypto"],"backgroundTag":"canonicalization-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}