Files
alknet/tasks/http/gateway/error-mapping.md
glm-5.2 e855c8c7eb docs(http): decompose alknet-http spec into 19 implementation tasks
Break the alknet-http architecture spec into atomic, dependency-ordered
tasks in tasks/http/, following the taskgraph frontmatter conventions
used by the call/core/vault crates.

Tasks span 7 phases across 5 module subdirectories (server/, gateway/,
client/, adapters/, websocket/):
- Phase 0: crate-init (foundation)
- Phase 1: gateway-dispatch-spine, error-mapping, shared-http-client
  (shared infrastructure)
- Phase 2: http-adapter, bearer-auth-middleware, gateway-endpoints,
  healthz-decoy (HTTP server surface)
- Phase 3: to-openapi (OpenAPI gateway projection)
- Phase 4: from-openapi (OpenAPI adapter, reqwest forwarding)
- Phase 5: dispatcher-transport-abstraction, upgrade-handler,
  connection-overlay (WebSocket browser bidirectional path)
- Phase 6: from-mcp, to-mcp (MCP adapters, feature-gated)
- Phase 7: review-http, review-websocket, review-mcp, review-http-final
  (quality checkpoints)

The gateway-dispatch-spine task implements the thin shared core
recommended by the gateway-factoring research (concrete struct, not a
trait). The dispatcher-transport-abstraction task is a cross-crate
change to alknet-call (exposes EventEnvelope-level dispatch API for
non-QUIC transports) — the highest-risk task. WebTransport/h3 is
deferred per ADR-044 and has no tasks; from_wss is out of scope.

Validated: 19 tasks, no cycles, 8 parallel generations, critical path
length 8 (through the WebSocket strand).
2026-07-01 07:11:17 +00:00

5.9 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
http/gateway/error-mapping Implement CallError-to-HTTP-status error mapping (ADR-023) pending
http/crate-init
narrow low component implementation

Description

Implement the CallError code → HTTP status code mapping in src/gateway/error.rs. This is the error-mapping table the HTTP server's gateway endpoints use to translate call-protocol CallError codes into HTTP response status codes (ADR-023). The mapping is a two-way-door default (the exact status for ambiguous codes can be refined additively); the one-way constraint is that protocol-level and operation-level codes are distinct (ADR-023) and from_openapi-imported codes are prefixed HTTP_<status> to avoid collision with protocol codes.

The mapping table (http-server.md §"Error Mapping")

Call code HTTP status Notes
NOT_FOUND (operation not registered, or Internal op) 404
FORBIDDEN (insufficient scopes, or unauthenticated) 401 (no token) / 403 (token present)
INVALID_INPUT (schema mismatch) 422
TIMEOUT 504 retryable: true
INTERNAL 500
Operation-level domain code with http_status (ADR-023) the declared http_status from_openapi-imported ops carry the original status
Operation-level domain code without http_status 500

The HTTP_<status> prefix rule (ADR-023 §5)

from_openapi maps OpenAPI non-2xx response status codes to ErrorDefinitions with codes prefixed HTTP_ + the status number:

// OpenAPI: 404: { schema: NotFoundError }
// → ErrorDefinition { code: "HTTP_404", http_status: Some(404), schema: NotFoundError }

The normative rule (review #002 W20): from_openapi must not produce error codes that collide with the five protocol-level codes (NOT_FOUND, FORBIDDEN, INVALID_INPUT, INTERNAL, TIMEOUT). The HTTP_<status> prefix enforces this.

retryableRetry-After hint

The retryable field from CallError maps to an HTTP Retry-After hint for 503/429-class errors (operation-level codes with http_status in that range). The hint is optional; if the operation-level error does not carry a retry-after value, no header is added.

API

/// Map a CallError to an HTTP status code (ADR-023).
pub fn call_error_to_http_status(error: &CallError) -> u16;

/// Map a CallError to an HTTP response, including the Retry-After hint
/// when applicable. The body is the serialized CallError (or its
/// `details` field).
pub fn call_error_to_http_response(error: &CallError) -> axum::response::Response;

The FORBIDDEN case needs the caller's identity state to distinguish 401 (no token) from 403 (token present but insufficient scopes). The mapping function takes an Option<Identity> (or a flag) so the gateway endpoint can pass the resolved identity through:

/// Map a CallError to an HTTP status code, considering whether the caller
/// was authenticated (FORBIDDEN → 401 if no identity, 403 if identity
/// present but insufficient scopes).
pub fn call_error_to_http_status_with_identity(
    error: &CallError,
    identity: Option<&Identity>,
) -> u16;

What this task does NOT do

  • No to_openapi error projection. to_openapi projects error_schemas to the gateway endpoint's response definitions (the OpenAPI doc's responses block). That is the to-openapi task, not this one. This task is the runtime HTTP response mapping.
  • No from_openapi error import. from_openapi builds ErrorDefinitions from OpenAPI non-2xx responses with the HTTP_<status> prefix. That is the from-openapi task. This task consumes the resulting CallError codes at runtime.

Acceptance Criteria

  • call_error_to_http_status(error: &CallError) -> u16 implemented
  • NOT_FOUND → 404
  • FORBIDDEN → 401 (no identity) / 403 (identity present)
  • INVALID_INPUT → 422
  • TIMEOUT → 504
  • INTERNAL → 500
  • Operation-level code with http_status → declared status
  • Operation-level code without http_status → 500
  • HTTP_<status>-prefixed codes (from from_openapi) → the status number
  • call_error_to_http_response(error) builds an axum::response::Response with the status + JSON body
  • retryable: true on 503/429-class errors → Retry-After header (when value present)
  • call_error_to_http_status_with_identity(error, identity) for the 401/403 split
  • Unit test: each protocol code maps to the correct status
  • Unit test: operation-level code with http_status maps to declared status
  • Unit test: operation-level code without http_status maps to 500
  • Unit test: HTTP_404 code maps to 404 (not collided with protocol NOT_FOUND)
  • Unit test: FORBIDDEN with None identity → 401
  • Unit test: FORBIDDEN with Some(identity) → 403
  • cargo test -p alknet-http succeeds
  • cargo clippy -p alknet-http --all-targets succeeds with no warnings

References

  • docs/architecture/crates/http/http-server.md — Error Mapping table (§"Error Mapping")
  • docs/architecture/crates/http/http-adapters.md — Error Fidelity (§"Error Fidelity (ADR-023)")
  • docs/architecture/decisions/023-operation-error-schemas.md — ADR-023 (protocol/operation codes distinct, HTTP_ prefix)

Notes

The mapping is a two-way-door default (the exact status for ambiguous codes can be refined additively); the one-way constraint is that protocol-level and operation-level codes are distinct (ADR-023) and from_openapi-imported codes are prefixed HTTP_. The FORBIDDEN case needs the caller's identity state to distinguish 401 (no token) from 403 (token present but insufficient scopes). This task is the runtime HTTP response mapping; the to_openapi doc-level error projection is the to-openapi task, and the from_openapi error import is the from-openapi task.

Summary

To be filled on completion