{"record":{"id":"979a41f2c9c5e078","repo":"mastra-ai/mastra","slug":"agent-card-signature-is-missing-a-protected-alg","errorCode":null,"errorMessage":"Agent Card signature is missing a protected \"alg\" header","messagePattern":"Agent Card signature is missing a protected \"alg\" header","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client-sdks/client-js/src/utils/verify-agent-card-signature.ts","lineNumber":115,"sourceCode":"    return agentCard;\n  }\n\n  const canonicalPayload = canonicalize(stripAgentCardSignatures(agentCard));\n  if (!canonicalPayload) {\n    throw new MastraClientError(200, 'OK', 'Failed to canonicalize A2A Agent Card for signature verification');\n  }\n\n  const allowedAlgorithms = options.algorithms ?? [...DEFAULT_AGENT_CARD_SIGNATURE_ALGORITHMS];\n  const encodedPayload = base64url.encode(canonicalPayload);\n  const verificationErrors: string[] = [];\n\n  for (const [index, signature] of signatures.entries()) {\n    try {\n      const compactJws = `${signature.protected}.${encodedPayload}.${signature.signature}`;\n      const protectedHeader = decodeProtectedHeader(compactJws);\n\n      if (typeof protectedHeader.alg !== 'string') {\n        throw new Error('Agent Card signature is missing a protected \"alg\" header');\n      }\n\n      if (!allowedAlgorithms.includes(protectedHeader.alg)) {\n        throw new Error(`Agent Card signature algorithm \"${protectedHeader.alg}\" is not allowed`);\n      }\n\n      const verificationKey = await options.keyProvider({\n        agentCard,\n        signature,\n        protectedHeader,\n        alg: protectedHeader.alg,\n        kid: typeof protectedHeader.kid === 'string' ? protectedHeader.kid : undefined,\n        jku: typeof protectedHeader.jku === 'string' ? protectedHeader.jku : undefined,\n        index,\n      });\n\n      if (!verificationKey) {\n        throw new Error('No verification key was provided for Agent Card signature verification');","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/client-sdks/client-js/src/utils/verify-agent-card-signature.ts#L97-L133","documentation":"Each Agent Card signature is a detached JWS. The library decodes the signature's `protected` header and requires a string `alg` header parameter before verifying. This error is thrown (and collected per-signature) when the protected header either has no `alg` or the `alg` is not a string, making it impossible to select a verification algorithm.","triggerScenarios":"getAgentCard() with verification enabled where the card's signatures[i].protected base64url-decodes to a JOSE header lacking a string `alg` (e.g. header is {\"typ\":\"JWT\"} or alg: null).","commonSituations":"Signer produced the JWS without an alg (rare, but possible with hand-rolled signers); a corrupted or truncated `protected` segment; a mock fixture with an empty/placeholder protected header; server library version producing headers this verifier rejects.","solutions":["Fix the signer so it includes a string `alg` in the protected header (e.g. ES256 or RS256).","Decode the `protected` segment (base64url → JSON) to confirm what header the server is actually emitting.","Regenerate the Agent Card signature with a standard JOSE library (jose, jsonwebtoken) rather than a custom encoder.","If the header is corrupted in transit, check for proxies/gateways rewriting the response body."],"exampleFix":"// before: header without alg\nconst protectedHeader = { typ: 'JWT' };\n\n// after\nconst protectedHeader = { alg: 'ES256', typ: 'JWT' };","handlingStrategy":"validation","validationCode":"// pre-flight: decode each signature's protected header and require a string alg\ncard.signatures?.forEach((sig, i) => {\n  const header = JSON.parse(atob(sig.protected.replace(/-/g, '+').replace(/_/g, '/')));\n  if (typeof header.alg !== 'string') {\n    throw new Error(`signature ${i} has no alg header; fix the signer before verification`);\n  }\n});","typeGuard":"function hasAlgHeader(h: unknown): h is { alg: string } & Record<string, unknown> {\n  return typeof h === 'object' && h !== null && typeof (h as any).alg === 'string';\n}","tryCatchPattern":"try {\n  await client.getAgentCard();\n} catch (e) {\n  if (e instanceof MastraClientError && e.message.includes('missing a protected \"alg\" header')) {\n    // fall back to unverified card or surface a clear config error\n  }\n}","preventionTips":["Sign Agent Cards with a standard JOSE library so the protected header always includes alg.","Decode and inspect the protected header of served signatures during server-side integration tests.","Reject placeholder/mock signatures in development fixtures."],"tags":["jws","jose-header","agent-card","signature-verification"],"backgroundTag":"missing-jws-alg-header","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}