RFC 9457 Error Handling & Typed SDK Exceptions
10. RFC 9457 Problem Details & Typed SDK Error Handling
Section titled “10. RFC 9457 Problem Details & Typed SDK Error Handling”All error responses from DID.is return application/problem+json conforming to RFC 9457.
Standard Error Envelope
Section titled “Standard Error Envelope”{ "type": "https://www.w3.org/ns/did#NOT_FOUND", "title": "Not found", "status": 404, "detail": "https://example.com/.well-known/did.json returned HTTP 404", "code": "NOT_FOUND", "trace": [ { "stage": "web.fetch", "label": "Fetch DID document", "status": "FAIL", "startedUs": 140, "durationUs": 48210, "detail": "HTTP 404 Not Found" } ]}Error Code Mapping
Section titled “Error Code Mapping”Problem code |
Problem type Base |
HTTP Status | Root Cause |
|---|---|---|---|
INVALID_DID |
https://www.w3.org/ns/did# |
400 | Syntax violates W3C ABNF parsing. |
INVALID_DID_URL |
https://www.w3.org/ns/did# |
400 | Malformed fragment, query, or path characters. |
INVALID_OPTIONS |
https://www.w3.org/ns/did# |
400 | Unknown query parameters or options. |
NOT_FOUND |
https://www.w3.org/ns/did# |
404 | Target DID document or URL resource does not exist. |
REPRESENTATION_NOT_SUPPORTED |
https://www.w3.org/ns/did# |
406 | Client requested an unsupported Accept header. |
METHOD_NOT_SUPPORTED |
https://www.w3.org/ns/did# |
501 | Method not implemented (supported: key, jwk, web, webvh). |
FEATURE_NOT_SUPPORTED |
https://www.w3.org/ns/did# |
501 | Path dereferencing (did:.../path) is unsupported. |
INVALID_DID_DOCUMENT |
https://www.w3.org/ns/did# |
422 | Document id does not match requested DID. |
INTERNAL_ERROR |
https://www.w3.org/ns/did# |
500 | Unexpected core panic or database failure. |
EGRESS_BLOCKED |
https://did.is/problems# |
403 | Target IP blocked by anti-SSRF security policy. |
UPSTREAM_UNAVAILABLE |
https://did.is/problems# |
502 | Upstream host reset connection or timed out (>4s). |
INVALID_REQUEST |
https://did.is/problems# |
400 | Duplicate JSON member names or malformed request body. |
PAYLOAD_TOO_LARGE |
https://did.is/problems# |
413 | Ingress body $>512\text{ KiB}$ or upstream body exceeded cap. |
UNAUTHORIZED |
https://did.is/problems# |
401 | Missing or invalid Bearer token. |
RATE_LIMITED |
https://did.is/problems# |
429 | Client exceeded rate budget. Check Retry-After. |
TypeScript SDK Error Typing & Narrowing
Section titled “TypeScript SDK Error Typing & Narrowing”The TypeScript SDK exports the canonical Problem interface and DidisError class for type-safe error handling:
export interface TraceStage { stage: string; label: string; status: "PASS" | "FAIL" | "WARN" | "INFO" | "SKIP" | string; startedUs: number; durationUs: number; detail: string;}
export interface Problem { type: string; title: string; status?: number; detail: string; code?: string; trace?: TraceStage[]; /** Retry-After delta-seconds or HTTP-date verbatim from the server */ retryAfter?: string;}
export class DidisError extends Error { readonly problem: Problem; readonly status: number;}Typed Error Handling Example
Section titled “Typed Error Handling Example”import { DidisClient, DidisError } from "@didis/client";
const didis = new DidisClient();
try { await didis.resolve("did:web:unknown-domain-404.example");} catch (err) { if (err instanceof DidisError) { const { code, status, detail, retryAfter } = err.problem;
switch (code) { case "NOT_FOUND": console.warn(`Identifier not found (HTTP ${status}): ${detail}`); break; case "RATE_LIMITED": console.warn(`Rate limited. Exponential backoff for ${retryAfter ?? 60} seconds.`); break; case "EGRESS_BLOCKED": console.error(`Security violation: Target domain resolved to a forbidden IP address.`); break; default: console.error(`API Error [${code ?? "UNKNOWN"}]: ${detail}`); break; } } else { console.error("Non-API network or environment error:", err); }}Python SDK Error Typing & Exception Handling
Section titled “Python SDK Error Typing & Exception Handling”The Python SDK raises DidisError, which encapsulates the HTTP status code and full RFC 9457 problem dictionary:
from typing import TypedDict, Optional, List, Dict, Any
class TraceStage(TypedDict, total=False): stage: str label: str status: str startedUs: int durationUs: int detail: str
class ProblemDetails(TypedDict, total=False): type: str title: str status: int detail: str code: str trace: List[TraceStage] retryAfter: Optional[str]
class DidisError(Exception): status: int problem: Dict[str, Any]Typed Error Handling Example
Section titled “Typed Error Handling Example”from didis import DidisClient, DidisError
with DidisClient() as client: try: res = client.resolve("did:web:unknown-domain-404.example") except DidisError as err: problem = err.problem code = problem.get("code") status = err.status detail = problem.get("detail", "") retry_after = problem.get("retryAfter")
if code == "NOT_FOUND": print(f"DID Document was not found ({status}): {detail}") elif code == "RATE_LIMITED": print(f"Rate limited. Back off for {retry_after or 60}s.") elif code == "EGRESS_BLOCKED": print(f"Anti-SSRF violation: Host resolved to a private/loopback IP.") else: print(f"DID.is API Error [{code}]: {detail}") except Exception as exc: print(f"Transport/System exception: {exc}")