Files
clean-architecture-backend-…/docs/superpowers/specs/2026-08-02-client-safe-error-boundary-design.md
DongHyeonkaandClaude Opus 5 5f10b791d3 chore: record pre-existing uncommitted repository state
Snapshot of the in-flight state that already existed, identically, in both
this worktree and the main checkout before this session began: the initial
HTTP Client platform implementation (previously untracked), the redis-lab
removal, and the JPA / object-storage / notification integration work.

Kept separate from this session's HTTP Client review response, which lands
in the following commit, so the two bodies of work stay reviewable apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 16:48:43 +09:00

3.8 KiB

Client-Safe Error Boundary Design

Date: 2026-08-02 Status: approved by the user's instruction to apply the detailed P1/P2 review sequentially Scope: HTTP error envelopes in adapter:inbound:web and the sample-portfolio domain advice

Context

Several handlers pass Exception#getMessage(), rejected request values, or a raw request URL into the public error envelope. Those values are not a stable API contract and can contain identifiers, tokens, uploaded values, configuration details, or internal diagnostics. Persistence and outbound dependency failures already use fixed client-safe messages; the rest of the HTTP boundary must follow the same rule.

Decision

The inbound adapter owns a message allowlist keyed by stable error code. Handlers may expose only:

  • stable code, category, HTTP status, and retryable from ApiErrorCode;
  • fixed, code-specific client messages;
  • bounded structural details such as field name, validation reason code, expected Java type, supported HTTP methods, or supported media types.

They must not expose exception messages, rejected values, raw request URLs, adapter/configuration diagnostics, opaque cursors, authentication diagnostics, resource identifiers, or duplicate domain values. Bean Validation interpolated/default messages are also discarded because custom templates can include the validated value. Validation details contain only normalized server-owned property names plus allowlisted reason codes and fixed messages; collection/map keys and indices are removed.

ClientSafeErrorMessages is extended for skeleton-wide operational codes. The sample keeps its domain wording in a separate package-private PortfolioClientSafeErrorMessages, preserving the rule that production modules do not know sample business concepts.

Public Messages

Representative mappings are fixed as follows:

  • MAPPING_FAILEDRequest data could not be mapped;
  • BAD_PARAMETERRequest parameter is invalid;
  • INVALID_TOKENAuthentication token is invalid;
  • UNAUTHENTICATEDAuthentication is required;
  • authorization denials → Access is denied;
  • PRECONDITION_FAILEDResource state changed; refresh and retry;
  • page/cursor failures → generic corrective text, with safe field/reason details retained;
  • ADAPTER_DISABLED and internal classifications → Internal server error;
  • domain not-found/conflict/invariant codes → fixed noun-level text with no ID/title value.

Transport overrides use fixed wording and retain only safe protocol metadata. For example, 405 still emits Allow, while both controller-route (NoHandlerFoundException) and static-resource (NoResourceFoundException) 404s use the same envelope without echoing the request URL.

Testing

Tests inject conspicuous secret sentinels into exception messages, rejected values, URLs, tokens, IDs, and duplicate titles. Every resulting response must preserve its status/code/category while excluding the sentinel from both error.message and error.details.

Validation tests additionally place sentinels in interpolated/default messages and iterable keys/indices. A real MockMvc resource-resolution request verifies the Spring 7 NoResourceFoundException path rather than calling the advice method directly.

The focused module suites remain the primary verification:

  • :adapter:inbound:web:test for operational and transport handlers;
  • :sample-portfolio:test for domain advice and sample wire behavior;
  • verifyCleanArchitectureDependencies for dependency direction.

Non-Goals

  • no change to error codes, categories, statuses, or retryability;
  • no suppression of server-side logs or tracing in this batch;
  • no application/domain dependency on HTTP response types;
  • no generic exception-message sanitizer based on regexes or truncation;
  • no staging, commit, amend, or push by an agent.