Problem Reports
Problem Reports
When something goes wrong during message delivery, the node sends a DIDComm problem report back to the sending agent. Problem reports are asynchronous — similar in purpose to HTTP status codes, but decoupled from the original request. Your agent receives them as ordinary messages.
Message Format
Every problem report is a standard DIDComm message with type https://didcomm.org/report-problem/2.0/problem-report:
{ "type": "https://didcomm.org/report-problem/2.0/problem-report", "id": "a-unique-message-id", "pthid": "id-of-the-message-that-caused-the-problem", "body": { "code": "e.p.trust.auth.not-authorized", "comment": "Not authorized to perform {1}", "args": ["https://acme.com/protocols/orders/1.0/query"], "escalate_to": "mailto:admin@example.com" }}| Field | Description |
|---|---|
code | Machine-readable problem code (see tables below) |
comment | Human-readable description with {1}, {2} placeholders |
args | Values to substitute into the comment template |
escalate_to | Optional URI for human escalation (e.g., mailto: or https://) |
pthid | Parent thread ID — links back to the message that caused the problem |
Code Format
Problem codes follow the DIDComm convention: {sorter}.{scope}.{descriptor}.
| Segment | Values | Meaning |
|---|---|---|
| Sorter | e = error, w = warning | Severity |
| Scope | p = protocol, m = message | Where the problem occurred |
| Descriptor | dotted path (e.g., trust.auth.not-authorized) | What went wrong |
Your agent can match on prefixes rather than exact codes. For example, handle all e.p.trust.* reports the same way — any trust-related error, regardless of whether the specific cause was an expired key, missing signature, or policy rejection.
Trust Problems
Authorization or authentication failures. These indicate the sender’s identity could not be verified, or the sender is not authorized for the requested action.
| Code | Description | Args |
|---|---|---|
e.p.trust.auth.not-authenticated | Sender could not be authenticated | — |
e.p.trust.auth.not-authorized | Sender is not authorized to perform the requested action | {1} = action |
e.p.trust.key.expired | Sender’s key has expired | {1} = key ID, {2} = expiration time |
e.p.trust.key.not-found | Sender’s key could not be found in their DID Document | {1} = key ID |
e.p.trust.sig.invalid | Message signature failed verification | {1} = message ID, {2} = key ID |
e.p.trust.sig.missing | Message is missing a required signature | {1} = message ID |
Common causes: The sender’s DID Document has a rotated or expired key, a policy rejects the sender’s DID or message type, or the sender lacks a required grant or credential.
DID Problems
The recipient DID could not be resolved or is invalid.
| Code | Description | Args |
|---|---|---|
e.p.did.invalid | The DID is malformed or uses an unsupported method | {1} = DID |
e.p.did.not-found | The DID does not exist | {1} = DID |
e.m.did.resolution-failed | The DID could not be resolved (e.g., DNS failure, unreachable host) | {1} = DID, {2} = reason (optional) |
Common causes: Typo in the DID, the recipient’s domain is unreachable, or the DID Document is not being served at the expected URL.
Transfer Problems
The message reached the recipient’s node but could not be delivered to the intended agent.
| Code | Description | Args |
|---|---|---|
e.p.xfer.no-handler | No agent is registered to handle messages for this recipient | {1} = recipient DID |
e.p.xfer.unknown | An unclassified delivery error occurred | {1} = error description |
Common causes: The recipient agent is not running or has not registered a handler for the DID, or a transport-level failure occurred between the node and the agent.
Internal Problems
The recipient’s node encountered an internal error while processing the message.
| Code | Description | Args |
|---|---|---|
e.p.me.internal-error | The node hit an unexpected error | — |
These are rare. If you receive one, the issue is on the recipient’s side — there’s nothing to fix on yours.
Handling Problem Reports
Register a handler for the problem report message type:
client.Handle("https://didcomm.org/report-problem/2.0/problem-report", func(msg *layr8.Message) (*layr8.Message, error) { code := msg.Body["code"].(string) switch { case strings.HasPrefix(code, "e.p.trust"): // Authorization or authentication failure case strings.HasPrefix(code, "e.p.did"): // DID resolution failure case strings.HasPrefix(code, "e.p.xfer"): // Delivery failure case strings.HasPrefix(code, "e.p.me"): // Recipient's internal error } return nil, nil })A few guidelines:
- Retry on transient failures.
e.p.xfer.*ande.p.me.*may be transient — the recipient’s agent or node may recover. Retry with backoff. - Don’t retry on trust failures.
e.p.trust.*means something about identity or authorization is wrong. Retrying the same message won’t help — the sender needs to fix the underlying issue (rotate keys, request a grant, etc.). - Don’t retry on DID failures.
e.p.did.*means the recipient DID is wrong or unresolvable. Fix the DID before retrying. - Log everything. Problem reports are your primary debugging tool for cross-organization issues. Log the
code,pthid, andargsfor troubleshooting.
Agent-Originated Problem Reports
Agents can also send problem reports. If your agent receives a message it cannot process — invalid payload, unsupported operation, business logic rejection — it can return a problem report to the sender using any code that makes sense for the situation.
The codes listed above are generated by the node infrastructure. Your agent is free to define application-specific codes following the same {sorter}.{scope}.{descriptor} convention. For example, an order processing agent might return e.p.order.invalid-quantity if a requested quantity exceeds available inventory.
When your agent returns an error from a message handler, the node automatically wraps it in a problem report and sends it back to the sender.