ruvnet/ruflo · error
mcp-caller-auth-verification-failed
mcp-caller-auth-verification-failed
Error message
mcp-caller-auth-verification-failed:${result.reason} What it means
The final check in resolveMcpCallerIdentity cryptographically verifies the invocation token against the caller's public key. When verifyInvocationToken returns valid=false, the error includes the verifier's reason (e.g. expired, bad signature) as mcp-caller-auth-verification-failed:<reason>, so the suffix tells you exactly which verification step failed.
Solutions
- Read the reason suffix in the error message and address that specific failure
- Re-issue the invocation token signed by the key matching CLAUDE_FLOW_MCP_CALLER_PUBKEY
- Check for clock skew (NTP) if the reason indicates expiry
- Rotate both token and pubkey together after key rotation
Example fix
// before # token signed by old key, pubkey = new key // after # re-issue token with new key, export matching CLAUDE_FLOW_MCP_INVOCATION_TOKEN and CLAUDE_FLOW_MCP_CALLER_PUBKEY
Defensive patterns
Strategy: try-catch
Try / catch
try { await callMcpTool(tool, args); } catch (e) { const m = /^mcp-caller-auth-verification-failed:(.+)$/.exec(e.message); if (m) { const reason = m[1]; if (reason.includes('expired')) reissueToken(); else if (reason.includes('signature')) rotateKeyAndToken(); } throw e; } Prevention
- Re-issue tokens after any keypair rotation
- Run NTP so token TTLs are evaluated correctly
- Ensure token callerId and configured pubkey belong to the same identity
- Monitor error reasons in logs to catch recurring expiry vs signature mismatches
When it happens
Trigger: A well-formed token and pubkey are both set, but verifyInvocationToken rejects them: signature mismatch, expired token, wrong audience/nonce, or the token was signed by a different key than the configured pubkey.
Common situations: Rotating the caller keypair without re-issuing the token; clock skew making a fresh token appear expired; reusing a token past its TTL; configuring the pubkey of a different agent than the token's signer.
Related errors
- mcp-caller-auth-enabled-but-no-token
- policy administration requires an authenticated user context
- policy- : ; receipt=
- RUFLO_X_ADMIN_TOKEN is not set (admission is admin-gated)
- approval issuance requires an authenticated human identity…
AI-assisted analysis of ruvnet/ruflo@2602b642d9 (2026-09-15).
Data as JSON: /api/errors/af63ae7c0dfe41aa.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/services/policy-runtime.ts:370
if (!encodedToken || !publicKeyHex) {
throw new Error('mcp-caller-auth-enabled-but-no-token');
}
const token = decodeTokenEnvelope(encodedToken);
if (!token) {
throw new Error('mcp-caller-auth-enabled-but-no-token');
}
let publicKey;
try {
publicKey = publicKeyFromHex(publicKeyHex);
} catch {
throw new Error('mcp-caller-auth-enabled-but-no-token');
}
const result = verifyInvocationToken(token, publicKey, {});
if (!result.valid) {
throw new Error(`mcp-caller-auth-verification-failed:${result.reason}`);
}
return { id: token.callerId, type: 'agent' };
}
export async function authorizeMcpTool(
toolName: string,
input: Record<string, unknown>,
context: Record<string, unknown> = {},
attributes: Readonly<{
actionType?: string;
network?: boolean;
destructive?: boolean;
namespaceAccess?: 'read' | 'write';
envelope?: CapabilityEnvelope;
costUsd?: number;
tokens?: number;
concurrency?: number;View on GitHub (pinned to 2602b642d9)