Errors
Error surface
The SDK exposes typed errors at two levels. DvmBridgeError comes from DvmClient operations. MobileNodeError comes from DvmMobileNode direct calls. Both map to clear recovery paths.
Use the error kind and message as product state. The kind indicates which recovery path to take; the message carries context to correlate with logs.
The reason-code table below is a different layer: the typed SDK errors here are the local surface a caller catches, while the runtime reason codes (POLICY_DENIED, REMOTE_TIMEOUT, PUBLICATION_DENIED, and more) are the runtime causes carried in receipts and logs.
Reason-code registry
Failure states
Reason codes align SDK errors, runtime receipts, retry policy, evidence artifacts, and operator action.
Policy admissionDenied
Do not retry until approval, limit, or display binding changes.
AdmissionDecision, policy hash, denial reason
Route to approval owner or product policy review.
Protocol planePendingRemote
Resume with the same operation id and participant set.
Peer wait record, deadline, last valid ingress hash
Check peer delivery, relay health, and participant availability.
Secret kernelFailed
Do not retry the local step; abort or rotate the session.
PermitEvent, local artifact state, request digest
Quarantine local artifact and investigate replay or duplicate execution.
Publication gateExecuted
Retry publication only after publication policy changes.
PublicationReceipt denial, disclosure scope, result reference
Keep local result closed and route to publication approver.
Validated ingressFailed
Do not retry the conflicting operation.
Both signed peer facts, slot, sender, session id
Escalate peer conflict and preserve the evidence bundle.
Local signer boundaryUnavailable
Retry only after runtime, JNI, HSM, or vault health is restored.
Boundary health state, call id, platform error kind
Fix local runtime packaging, vault access, or HSM availability.
Client-level errors (DvmBridgeError)
policyRejected(reason) fires when DvmPolicyProvider.evaluate returns approved: false. Treat it as product state: record the rejection reason for support, gate retry on a policy change rather than on time, and never retry it automatically without a state change. unsupported(reason) fires on designed-but-not-yet-wired calls — currently backups.recover(), backups.rotateCredential(), operations.cancel(), and operations.resume().
unavailable(message) indicates the bridge is not open or has shut down. ffi(code:message:) is a native runtime error; the code is a stable Rust error key suitable for log correlation. invalidIdentifier(message) fires on malformed operation or key id values.
Signer-level errors (MobileNodeError)
unavailable usually points to native library loading or platform packaging. ffi points to a native runtime error from the Rust side. invalidResponse points to an ABI shape mismatch between the Swift wrapper and the native library version. bindingMismatch points to a result that belongs to different public facts than what the binding expected. storage points to vault path or keyshare file availability.
Show packaging and availability problems as service health states; they are infrastructure failures. Show storage problems as key recovery or re-enrollment flows. Treat bindingMismatch as a security-relevant event: persist the public context (error kind, operation id, key id, public key) for investigation. Route bindingMismatch and production eligibility failures to release gates instead of retrying.