Skip to content

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"
}
}
FieldDescription
codeMachine-readable problem code (see tables below)
commentHuman-readable description with {1}, {2} placeholders
argsValues to substitute into the comment template
escalate_toOptional URI for human escalation (e.g., mailto: or https://)
pthidParent thread ID — links back to the message that caused the problem

Code Format

Problem codes follow the DIDComm convention: {sorter}.{scope}.{descriptor}.

SegmentValuesMeaning
Sortere = error, w = warningSeverity
Scopep = protocol, m = messageWhere the problem occurred
Descriptordotted 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.

CodeDescriptionArgs
e.p.trust.auth.not-authenticatedSender could not be authenticated
e.p.trust.auth.not-authorizedSender is not authorized to perform the requested action{1} = action
e.p.trust.key.expiredSender’s key has expired{1} = key ID, {2} = expiration time
e.p.trust.key.not-foundSender’s key could not be found in their DID Document{1} = key ID
e.p.trust.sig.invalidMessage signature failed verification{1} = message ID, {2} = key ID
e.p.trust.sig.missingMessage 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.

CodeDescriptionArgs
e.p.did.invalidThe DID is malformed or uses an unsupported method{1} = DID
e.p.did.not-foundThe DID does not exist{1} = DID
e.m.did.resolution-failedThe 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.

CodeDescriptionArgs
e.p.xfer.no-handlerNo agent is registered to handle messages for this recipient{1} = recipient DID
e.p.xfer.unknownAn 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.

CodeDescriptionArgs
e.p.me.internal-errorThe 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.* and e.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, and args for 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.