Rust Backend API Error Conventions
Backend HTTP errors should return the shared ApiError body shape:
{ "error": { "code": "STABLE_MACHINE_CODE", "message": "Safe user-facing message", "details": {} }, "request_id": "caller-visible-correlation-id"}code and message live inside the error object; they must not be emitted
at the top level. Every JSON 4xx/5xx response also receives the request ID used
by the response x-request-id header.
The outer HTTP request layer canonicalizes any remaining legacy JSON error
response before it reaches the wire. Route code should still construct an
ApiError directly; the boundary normalization is the safety net for protocol
adapters and older handlers, not a second response format.
Use apps/rust-server/crates/arinova-foundation/src/api_error_codes.rs for stable machine-readable
codes. Do not introduce route-local sentinel strings when a shared code already
exists. New codes should be uppercase snake case and should describe the client
contract, not a database or implementation detail.
The error-code contract test freezes legacy uppercase literals in a baseline.
Any newly introduced code literal must be declared in api_error_codes.rs;
updating the baseline is reserved for a reviewed non-error literal exemption.
Client responses must not include raw database, SQLx, Redis, provider, or
internal exception text. Log those details with tracing and return a safe
message through ApiError::internal or an explicit ApiError::new mapping.
The reviewed operator-authored message for a 503 MAINTENANCE response is the
only server-error message preserved by the outer request boundary; every other
5xx message is replaced with the generic internal-error message.
Likewise, the boundary wraps non-JSON framework 413 rejections in the generic
PAYLOAD_TOO_LARGE shape, but preserves reviewed route-specific JSON error
codes such as media validation limits.
OAuth and provider callbacks often use sentinel errors across service and route
boundaries. Those sentinels must come from api_error_codes so callback routes,
tests, and frontend handling do not drift independently.
Build a7f47a5ca54ddcf7806cd48b81ce1b9827042766