Architecture

Error Code Architecture

DSAR uses package-owned error catalogs with globally unique identifiers.

  • Each package owns its catalog in src/types/error-codes.ts.
  • @dsar/internals-error-codes is factory-only (createErrorRegistry, createErrorCodeSchema, and shared types).
  • Docs coverage and ID uniqueness are validated by scripts/check-error-doc-coverage.mjs.

Namespace Registry

packageid prefixfallback code example
@dsar/backendDSAR-BE-*INTERNAL_UNCATALOGED_ERROR
@dsar/node-sdkDSAR-SDK-*SDK_UNCATALOGED_ERROR
@dsar/cliDSAR-CLI-*CLI_UNCATALOGED_ERROR
@dsar/coreDSAR-CORE-*CORE_UNCATALOGED_ERROR
@dsar/policy-engineDSAR-PE-*POLICY_ENGINE_UNCATALOGED_ERROR
@dsar/policy-packsDSAR-PP-*POLICY_PACKS_UNCATALOGED_ERROR
@dsar/persistenceDSAR-PS-*PERSISTENCE_UNCATALOGED_ERROR
@dsar/persistence-pgDSAR-PG-*PERSISTENCE_PG_UNCATALOGED_ERROR
@dsar/persistence-sqliteDSAR-SQL-*PERSISTENCE_SQLITE_UNCATALOGED_ERROR
@dsar/storage-s3DSAR-S3-*STORAGE_S3_UNCATALOGED_ERROR
@dsar/storage-filesystemDSAR-FS-*STORAGE_FILESYSTEM_UNCATALOGED_ERROR
@dsar/storage-vercel-blobDSAR-VB-*STORAGE_VERCEL_BLOB_UNCATALOGED_ERROR
@dsar/inbound-resendDSAR-IN-*INBOUND_RESEND_UNCATALOGED_ERROR
@dsar/outbound-resendDSAR-OUT-*OUTBOUND_RESEND_UNCATALOGED_ERROR
@dsar/guardsDSAR-GRD-*GUARDS_UNCATALOGED_ERROR
@dsar/schemaDSAR-SCH-*SCHEMA_UNCATALOGED_ERROR

Backend Catalog

The canonical backend catalog is emitted by @dsar/backend from packages/backend/src/types/error-codes.ts.

Backend Envelope fields

All non-success responses include:

  • error.id - stable backend error identifier (DSAR-BE-xxxx)
  • error.code - semantic machine-readable code
  • error.docsUrl - canonical documentation link for the identifier (https://dsar-sdk.dev/errors/dsar-be-xxxx)
  • error.message - human-readable summary
  • error.status - HTTP status
  • error.trace - optional diagnostics-only payload (e.g. trace.lifecycle for transition errors)

Catalog

idcodestatusmeaningretriabledocs
DSAR-BE-1001AUTH_ACTOR_CONTEXT_MISSING401Required actor header is missing on a protected endpoint.nodocs/errors/dsar-be-1001.md
DSAR-BE-1002AUTH_APPROVER_ROLE_FORBIDDEN403Actor role is not allowed to perform an approver action.nodocs/errors/dsar-be-1002.md
DSAR-BE-1101REQUEST_BODY_INVALID_JSON400Request body cannot be parsed as valid JSON.nodocs/errors/dsar-be-1101.md
DSAR-BE-1102REQUEST_ROUTE_PARAM_MISSING400A route parameter required by the handler is missing.nodocs/errors/dsar-be-1102.md
DSAR-BE-1103REQUEST_BASE_PATH_INVALID400Runtime base path configuration is invalid.nodocs/errors/dsar-be-1103.md
DSAR-BE-1199REQUEST_VALIDATION_FAILED400Request validation failed without a narrower boundary tag.nodocs/errors/dsar-be-1199.md
DSAR-BE-1201REQUEST_ROUTE_NOT_FOUND404Incoming path/method does not map to any registered route.nodocs/errors/dsar-be-1201.md
DSAR-BE-1202POLICY_ACTIVATION_NOT_FOUND404No active policy activation was found for the given scope.nodocs/errors/dsar-be-1202.md
DSAR-BE-1203Reserved (unassigned).
DSAR-BE-1204DELIVERY_ARTIFACT_NOT_FOUND404No fulfilment artifact exists for the target request.nodocs/errors/dsar-be-1204.md
DSAR-BE-1205DELIVERY_TOKEN_INVALID403Delivery token is missing, expired, or does not match.nodocs/errors/dsar-be-1205.md
DSAR-BE-1206MANIFEST_ARTIFACT_UPLOAD_FAILED400Manifest artifact upload failed.nodocs/errors/dsar-be-1206.md
DSAR-BE-1207MANIFEST_ARTIFACT_DOWNLOAD_FAILED404Manifest artifact download failed.nodocs/errors/dsar-be-1207.md
DSAR-BE-1208MANIFEST_ARTIFACT_REPLACE_FAILED400Manifest artifact replacement failed.nodocs/errors/dsar-be-1208.md
DSAR-BE-1209FULFILMENT_MANIFEST_NOT_APPROVED409Manifest must be approved before fulfilling.nodocs/errors/dsar-be-1209.md
DSAR-BE-1210FULFILMENT_NO_ARTIFACTS409At least one artifact required to fulfil.nodocs/errors/dsar-be-1210.md
DSAR-BE-1301POLICY_JURISDICTION_UNMAPPED400No policy pack is mapped to the provided jurisdiction.nodocs/errors/dsar-be-1301.md
DSAR-BE-1302POLICY_ENFORCEMENT_REFUSAL_BLOCKED403Refusal blocked by active policy.nodocs/errors/dsar-be-1302.md
DSAR-BE-1303Reserved (unassigned).
DSAR-BE-1304RETENTION_CLASS_INVALID400Provided retention class is not a recognized value.nodocs/errors/dsar-be-1304.md
DSAR-BE-1401LIFECYCLE_TRANSITION_DISALLOWED409Requested lifecycle action is not allowed in current state.nodocs/errors/dsar-be-1401.md
DSAR-BE-1402LIFECYCLE_STATUS_UNKNOWN409Current lifecycle status value is unknown/unsupported.nodocs/errors/dsar-be-1402.md
DSAR-BE-1403LIFECYCLE_RATIONALE_MISSING400Lifecycle transition requires a non-empty rationale.nodocs/errors/dsar-be-1403.md
DSAR-BE-1500INTERNAL_RUNTIME_ERROR500Unhandled backend runtime exception.yesdocs/errors/dsar-be-1500.md
DSAR-BE-1599INTERNAL_UNCATALOGED_ERROR500Runtime emitted an unknown code; fallback mapping applied.yesdocs/errors/dsar-be-1599.md

Logging behavior

toErrorResponse (in packages/backend/src/middleware/errors.ts) logs all mapped and unmapped errors server-side using structured logs including:

  • id
  • code
  • status
  • docsUrl
  • request (method, path, query, sanitized headers, optional body preview)
  • sanitized error payload (only name, message, code, and stack in development)

Header and query redaction

Sensitive headers (authorization, cookie, proxy-authorization, set-cookie, x-api-key) are replaced with [REDACTED] before logging.

Query parameters matching sensitive names (token, secret, password, api_key, apikey, auth, access_token, key) are also redacted.

Body preview limits

Request body previews use two related constants: BODY_PREVIEW_LIMIT (default 4096 bytes) is the maximum number of bytes included in the preview text, and BODY_PREVIEW_MAX_BYTES (BODY_PREVIEW_LIMIT + 1) is the stream read cap that reads one extra byte to detect whether the body was truncated. Bodies exceeding the limit are either:

  • omitted with a descriptive message when content-length declares a size beyond BODY_PREVIEW_LIMIT, or
  • truncated after streaming up to BODY_PREVIEW_MAX_BYTES with a truncated: true flag in the log payload.

Binary content types (application/octet-stream, multipart/form-data) are excluded entirely with [binary content omitted]. GET and HEAD requests skip body capture.