Native library reference. Extended CLI examples use
jacs-compat. This source reference can include unreleased changes; see the package guide for installation and MCP profiles for available tools.
Failure Modes
This page documents the error messages you will see when multi-agent agreements fail, plus the P2 compatibility and export failure table for operators. The legacy diagnostics below inspect supplied metadata and signatures; they do not authenticate consent policy. Even a mathematically valid v1 report returns complete=false and policy_accepted=false. Native v2 also remains inspection-only.
Partial Signing (Agent Crash)
What happened: An agreement was created for N agents but one or more agents never signed -- they crashed, timed out, or disconnected before calling sign_agreement.
Error message:
not all agents have signed: ["<unsigned-agent-id>"] { ... agreement object ... }
What to do: Identify the unsigned agent from the error, re-establish contact, and have them call sign_agreement on the document. Additional signatures can be collected, but this does not turn unauthenticated legacy policy into authorization.
Quorum Not Met
What happened: An agreement with an explicit quorum (M-of-N via AgreementOptions) received fewer than M signatures.
Error message:
Quorum not met: need 2 signatures, have 1 (unsigned: ["<agent-id>"])
What to do: Treat this as a diagnostic of the supplied legacy quorum metadata, not an authenticated consent decision. The unsigned agent IDs in the error tell you exactly who still needs to sign.
Tampered Signature
What happened: A signature byte was modified after an agent signed the agreement. The cryptographic verification layer detects that the signature does not match the signed content.
Error message:
The message varies by algorithm and the first failed check. Legacy checking evaluates supplied quorum/missing-signer metadata before individual signatures, so an incomplete document may report that earlier diagnostic even if a signature is also corrupted. Do not infer that unexamined signatures are valid.
What to do: This indicates data corruption in transit or deliberate tampering. Discard the document and request a fresh copy from the signing agent. Do not attempt to re-sign a document with a corrupted signature.
Tampered Document Body
What happened: The document content was modified after signatures were applied. JACS stores an integrity hash of the agreement-relevant fields at signing time, and any body modification causes a mismatch.
Error message:
Agreement verification failed: agreement hashes do not match
What to do: The document body no longer matches what the agents originally signed. Discard the modified document and go back to the last known-good version. If the modification was intentional (e.g., an amendment), create a new agreement on the updated document and collect fresh signatures from all parties.
In-Memory Consistency After Signing
sign_agreement stores the resulting document before returning. Storage errors make the operation fail; a separate later save() is not required to make a successful return persistent in the configured backend. An in-memory backend remains nonpersistent by design.
After an error, inspect the actual stored state before retrying or broadcasting. Do not assume a failed call committed a complete signature update.
P2 Compatibility and Export Failures
Beyond agreements, the P2 trust model (native signing with a post-quantum default, the ES256 compatibility key, and targeted ecosystem exports) emits structured log events with matching Prometheus counters. This is the sysadmin fix-table: what fired, which metric to alert on, which part of the model is involved, and the usual fix.
Note on the Metric column: the counters exist only in builds with the
otlp-metrics feature enabled — it is not in the default feature set of
the jacs crate or of cargo install jacs-cli-compat, so in a default build the
counters compile to no-ops. The log events are always emitted once a
tracing subscriber is installed (see
Observability).
| Log event | Metric | Failure class | Likely fix |
|---|---|---|---|
compatibility_key_missing (WARN) | jacs_compatibility_export_error_total{format,reason="missing_key"} | compatibility binding | An export was attempted (requested_export in the event; format on the metric) but the agent has no ES256 compatibility key — run jacs-compat agent add-compat-key. |
compatibility_key_unreadable (WARN) | — (log event only, no counter) | compatibility binding | A gate-and-enrich export (did, w3c-agent-identity) found the ES256 key state corrupt or inconsistent — jacs.keyring.json unparseable, or the keyring and the key files disagree (reason in the event). The export still succeeds in its native-only shape (a corrupt keyring must not take down DID serving), so this WARN is the only signal — inspect jacs.keyring.json and the jacs.ecosystem.* key files in the key directory. Distinct from compatibility_key_missing, which is the quiet never-migrated state. |
compatibility_binding_verify_failed (WARN) | jacs_compatibility_binding_verify_failed_total{reason} | compatibility binding | The native-root-signed binding no longer verifies (jacs_id + compat kid in the event) — re-issue after key rotation (jacs-compat agent issue-compat-binding) and check expiresAt. Fixed reason values: schema_invalid (also covers a jacsSha256 content-hash mismatch — the document lies about itself), signature_invalid, rotated_root, root_kid_mismatch, compat_kid_mismatch, jwk_mismatch, identity_mismatch, version_mismatch (identity discovery safely reissues an otherwise-authentic same-root stale-version binding), superseded (an older validly-signed binding was restored over a re-issue; latest issuedAt wins), and expired (also an unparseable expiresAt, which fails closed). |
content_export_scope_denied (WARN) | jacs_content_export_scope_denied_total{format} | ecosystem export | The binding (binding_hash in the event) lacks the requested scope (ap2-mandate, agreement-vc are never auto-issued) — re-issue the binding with that scope; the current native root signs the grant. |
compatibility_binding_created (INFO) | — (log event only, no counter) | compatibility binding | Success, not a failure — the current native root (re-)issued the compatibility key binding (jacs_id, compat kid, binding_hash, and granted scopes in the event). Expect one per jacs-compat agent issue-compat-binding or first identity-export auto-issue; its absence after a key rotation means the required re-issue has not happened yet. |
ecosystem_export_generated (INFO) | jacs_compatibility_export_total{format} | ecosystem export | Success, not a failure — six formats, exactly the six binding scopes: jwks, did, a2a-agent-card, w3c-agent-identity, ap2-mandate, agreement-vc. |
Metric label values are fixed, low-cardinality strings (reason,
format) — never raw error text.
Two boundary notes for dashboard authors:
- The
didandw3c-agent-identityexports are gate-and-enrich views: without a valid binding granting the scope they still succeed in their pre-P2 native-only shape and emit no event or counter. Only the authorized (compat-enriched) exports appear underjacs_compatibility_export_total. One exception to the silence: when the compat key state exists but is unreadable (corrupt keyring, missing key file), the native-only fallback emits thecompatibility_key_unreadableWARN above. - Exporting the compatibility key binding itself (
jacs-compat agent export-compat-binding) is the trust artifact behind the six scoped exports, not one of them — it logs a plain INFO line and never appears under theformatlabel.
See Also
- Creating and Using Agreements - Agreement creation and signing workflow
- Security Model - Overall security architecture, including the compatibility key binding lifecycle
- Key Rotation - Rotation, Ed25519 migration, and binding re-issue
- Cryptographic Algorithms - Algorithm details and signature verification