CRUMB a card from devarno-cloud

Standard Error Envelope

so1 beginner 4 min read

ELI5

Every error ships in the same little box: a tracking number on the outside (requestId), a colour-coded label inside (code), a polite note for the customer (message), and a sealed envelope for engineers (details).

Technical Deep Dive

Shape

classDiagram
class ErrorEnvelope {
+requestId : string (uuid)
+error : ErrorBody
}
class ErrorBody {
+code : string
+message : string
+details? : Record~string,unknown~
}
ErrorEnvelope --> ErrorBody

Per ADR-004:

{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"error": {
"code": "FORBIDDEN",
"message": "You do not have permission to view this repository.",
"details": {
"upstream": {
"statusCode": 403,
"error": "Resource not accessible by integration",
"url": "https://api.github.com/repos/org/private-repo"
}
}
}
}

Envelope vs Raw Body

  • Errors are always wrapped in the envelope (parsed when !response.ok).
  • Success responses are raw payloads (e.g., { repos: [...] }) plus X-Request-Id header.

The client distinguishes by HTTP status, not by inspecting the body.

Field Rules

FieldAudienceNotes
requestIdsupport / engineersUUID v4, also in X-Request-Id and logs (so1-012)
error.codeclient logicmachine-readable, stable enum (so1-011)
error.messageend usernon-technical; bugs hidden in details
error.detailsengineersoptional; may contain redacted upstream payloads

Redaction (ADR-004)

error.message and error.details MUST never contain: API keys, tokens, secrets, raw stack traces, file paths, or DB query errors. The reference redactError strips Bearer …, sk-…, and /home/…/ patterns.

Key Terms

  • Envelope → wrapping object that adds metadata around a payload.
  • requestId → UUID tying envelope to logs and upstream traces.
  • details → free-form bag for engineer-only context.

Q&A

Q: Why are success responses not wrapped? A: ADR-004 explicitly trades wrapping consistency for smaller success payloads; clients still get tracing via the X-Request-Id response header.

Q: Is details required? A: No — it is optional. Auth errors typically omit it; upstream-mapped errors fill it.

Q: How is the envelope produced? A: ADR-004 names a buildErrorEnvelope() helper in @so1/shared/src/errors.ts invoked by the error handler middleware (so1-004).

Examples

A failing call from so1-rover/src/lib/api-client.ts parses { requestId, error }, throws ApiError(error.message, { code: error.code, requestId, details }). The error boundary renders the message plus the requestId so a user pasting it into Slack gives engineers a one-step path to the log line.

neighbors on the map