Skip to content

public-api-http: make Native error rendering usable outside space-api - #1364

Merged
NancyKwongPerplexity merged 1 commit into
mainfrom
native-error-rendering-shared
Nov 3, 2026
Merged

NancyKwongPerplexity merged 1 commit into
mainfrom
native-error-rendering-shared

Conversation

@NancyKwongPerplexity

@NancyKwongPerplexity NancyKwongPerplexity commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Moves the Native error contract out of space-api into a new crate, pplx-space-public-api-http (crates/l40_api/public/api/http), so another server answering Native routes renders the same envelopes, statuses and headers. The crate contains:

  • PublicApiError, with error_contract, its constructors, and IntoResponse into the Native envelope. That covers Retry-After, the public_api.response metric and the span classification.
  • The request-id middleware: echo a valid x-request-id, or issue req_<uuid>. The current id is exposed as current_request_id and scope_request_id.
  • normalize_public_error_response: framework rejections (405, 413, 415, 422) and unhandled error statuses become an envelope that keeps its status.
  • public_route_not_found, with_retry_after_hint, retry_after_header_value and LOCK_CONTENTION_RETRY_AFTER. controllers::substrate's constant now names the crate's, and its rationale stays where it is.

space-api re-exports these from routes::public::error and keeps the conversions from its own store errors. The Rust orphan rule does not allow impl From<sqlx::Error> for PublicApiError outside the crate that defines PublicApiError, so the 32 call sites that relied on it now call database_error. That function classifies a lock race as the retryable 503 and logs everything else, exactly as the impl did. The comments that named the impl now name the function. The file errors' conversions into PublicApiError move into pplx-space-native-files. classification_for_test becomes the public classification(), because a test-only accessor cannot cross a crate boundary.

No behaviour change.

Validation

At this head (1be4b2ad), on main at 0271415a:

  • Rebased onto native-files: share Native file request handling #1363's new head with no conflicts. git range-diff differs only in context: main (api: page and filter the admin sandbox list, count spaced versions, and share one sandbox ID parser #1321) changed the line above one database_error call in routes/admin/sandboxes.rs.
  • cargo fmt --all -- --check, scripts/generate-env-contract.sh --check, scripts/check-rust-error-policy.sh and scripts/source_guards/env_test_hook_boundary.py pass.
  • cargo clippy --locked --all-targets --all-features -p pplx-space-native-files -p pplx-space-api -p pplx-space-public-api-http -- -D warnings passes.
  • cargo check --workspace --all-targets --locked passes.
  • cargo arch against main's baseline, renames and ARCHITECTURE.toml: 0 required findings and 112 advisory, the same findings as main.
  • cargo nextest run --config 'profile.test.package.pplx-space-api.opt-level=0' --all-features --all-targets, one package at a time, with Postgres and Redis from the nextest setup scripts and SPACE_CI_REQUIRE_TEST_DEPS=1: pplx-space-api 2184 passed, 1 skipped; pplx-space-native-files 1 passed; pplx-space-public-api-http 9 passed.
  • Both new crates also pass clippy linted alone (cargo clippy -p <crate> --all-targets --all-features -- -D warnings, separate target directory), as the isolated-crates check does.

The error and request-id tests that need nothing from space-api move with the code. The lock-contention test for sqlx, operation-store and session-store errors, and the ended-session test, stay in space-api. They now read status and classification through classification() and retryable from the rendered envelope.

Severity and risk

Severity: does not apply (refactor).

Risk: a missed database_error site would fail to compile rather than change behaviour, because no conversion from sqlx::Error exists any more. The rendering code moves verbatim. To roll back, revert this PR.

Stack

This is PR 3 of 5 and depends on #1363 (native-files-shared-handling). Merge in order:

  1. native-files: share the file-request fingerprints and guest key #1362 (native-files-shared-request)
  2. native-files: share Native file request handling #1363 (native-files-shared-handling)
  3. this PR
  4. api: serve native file access RPCs on the activation listener #1365 (native-file-access-rpcs)
  5. auth: let the data-plane identity call the native file access RPCs #1371 (data-plane-native-file-access-authz)

AI assistance: Claude Code drafted the code, tests, and this description; the author reviewed them.

@nikhilbirmiwal-pplx nikhilbirmiwal-pplx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Diffed the new public-api-http error.rs/request_id.rs against the old space-api error.rs/request_context.rs: verbatim apart from visibility, the retry_after_header_value/LOCK_CONTENTION_RETRY_AFTER relocation (same value/logic), and the same request_id_from_headers (common_svc re-exports it). database_error matches the old Fromsqlx::Error exactly, the call-site rewrites are mechanical, and the file-error From impls moved unchanged; no behaviour change found.

@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the native-files-shared-handling branch from f99ebdc to 03f4885 Compare November 3, 2026 19:39
@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the native-error-rendering-shared branch from 0c7cba6 to 1be4b2a Compare November 3, 2026 19:39
@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the native-files-shared-handling branch from 03f4885 to 194d52c Compare November 3, 2026 20:09
Base automatically changed from native-files-shared-handling to main November 3, 2026 20:41
Move PublicApiError, its error contract, its rendering into the Native
envelope, the request-id middleware and the normalization of framework
rejections and unhandled error statuses out of space-api into a new
pplx-space-public-api-http crate in crates/l40_api, so another server
answering Native routes renders the same envelopes, statuses and headers.

space-api keeps the conversions from its own errors. The Rust orphan rule
does not allow it to keep `From<sqlx::Error> for PublicApiError` once the
type lives elsewhere, so its 32 `?` and `from` sites now call
`database_error`, which classifies and logs exactly as the impl did. The
file errors' conversions move into pplx-space-native-files.

The error and request-id tests that need nothing from space-api move with
the code; the database and session ones stay. No behaviour change.
@NancyKwongPerplexity
NancyKwongPerplexity force-pushed the native-error-rendering-shared branch from 1be4b2a to 41953a9 Compare November 3, 2026 20:43
@NancyKwongPerplexity
NancyKwongPerplexity added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 61f74dd Nov 3, 2026
41 checks passed
@NancyKwongPerplexity
NancyKwongPerplexity deleted the native-error-rendering-shared branch November 3, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants