Skip to content

feat(sdk,service): add typed ErrorCode enum, timeout/error helpers, and discovery - #337

Open
tholop wants to merge 1 commit into
feat/sdk-vm-labelsfrom
feat/sdk-typed-helpers
Open

tholop wants to merge 1 commit into
feat/sdk-vm-labelsfrom
feat/sdk-typed-helpers

Conversation

@tholop

@tholop tholop commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Lets SDK callers handle timeouts and missing VMs by exception type instead of matching error text, and find the local gateway without copying discovery code. The Inspect AI integration (#338) uses both. Stacked on #336.

Changes

  • Service & API (capsem-api, capsem-service, OpenAPI):
    • Adds machine-readable ErrorCode (vm_not_found, create_timeout, exec_timeout) to ErrorResponse (openapi.json and generated SDK models) and returns HTTP 504 (StatusCode::GATEWAY_TIMEOUT) with timeout_secs on guest command execution timeouts (AppError::exec_timeout).
    • Migrates AppError in crates/capsem-service/src/errors.rs from a tuple struct AppError(StatusCode, String) to AppError { status: StatusCode, body: ErrorResponse }, mechanically updating the ~182 (status, message) route-handler call sites to AppError::new(status, msg) alongside AppError::vm_not_found, AppError::exec_timeout, and AppError::create_timeout.
  • Python SDK (sdk/python):
    • Parses ErrorResponse once in HttpError.__init__ and raises typed VmNotFoundError, CreateTimeoutError, and ExecTimeoutError (the two timeout classes subclass CapsemTimeoutError(TimeoutError, CapsemError)), with pickle round-trip support (HttpError.__reduce__ / __setstate__ and Transport.__getstate__ dropping _session).
    • Adds host gateway discovery (discover_gateway, capsem_run_dir, Hypervisor.connect) to resolve CAPSEM_GATEWAY_URL / CAPSEM_GATEWAY_TOKEN or ~/.capsem/run/gateway.{port,token} ($CAPSEM_RUN_DIR) on the host before a gateway connection exists.

@codecov-commenter

codecov-commenter commented Oct 7, 2026 •

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
6052 2 6050 0
View the top 3 failed test(s) by shortest run time
capsem-service::bin/capsem-service::container_setup::tests::retirement::replaced_aborted_setup_cannot_escape_its_blocking_retirement_barrier
Stack Traces | 0.009s run time
thread 'container_setup::tests::retirement::replaced_aborted_setup_cannot_escape_its_blocking_retirement_barrier' (132721) panicked at .../container_setup/tests/retirement.rs:112:5:
idle generations must release retirement tracking
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
capsem-assets::oci::cache::inventory::tests::reused_materialization_controls_invalidate_inventory_on_acquire_and_after_release
Stack Traces | 0.271s run time
thread 'oci::cache::inventory::tests::reused_materialization_controls_invalidate_inventory_on_acquire_and_after_release' (134836) panicked at .../cache/inventory/tests.rs:130:39:
called `Result::unwrap()` on an `Err` value: cache changed during inventory observation
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
capsem-assets::oci::cache::inventory::tests::cancelled_partial_materialization_releases_its_barrier_before_invalidating_inventory
Stack Traces | 0.341s run time
thread 'oci::cache::inventory::tests::cancelled_partial_materialization_releases_its_barrier_before_invalidating_inventory' (134657) panicked at .../cache/inventory/tests.rs:187:39:
called `Result::unwrap()` on an `Err` value: cache changed during inventory observation
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
capsem-assets::oci::worker::tests::inventory_worker_publishes_only_quiet_bounded_observations_and_tracks_nested_changes
Stack Traces | 2.46s run time
thread 'oci::worker::tests::inventory_worker_publishes_only_quiet_bounded_observations_and_tracks_nested_changes' (139428) panicked at .../oci/worker/tests.rs:53:6:
called `Result::unwrap()` on an `Err` value: TimedOut { label: "changed-inventory", attempts: 8, timeout: 2s }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
capsem-assets::oci::worker::tests::incompatible_receipts_are_observed_once_until_their_metadata_changes
Stack Traces | 2.7s run time
thread 'oci::worker::tests::incompatible_receipts_are_observed_once_until_their_metadata_changes' (139378) panicked at .../oci/worker/tests.rs:246:10:
called `Result::unwrap()` on an `Err` value: TimedOut { label: "foreign-cache-reobserved", attempts: 8, timeout: 2s }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
capsem-core::fs_monitor::tests::a_workspace_swapped_for_a_host_link_is_never_walked_or_read
Stack Traces | 360s run time
No failure message available

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

…nd discovery

Add structured error codes (`ErrorCode::VmNotFound`, `ErrorCode::CreateTimeout`,
`ErrorCode::ExecTimeout`) to `ErrorResponse` in `capsem-api` and `capsem-service`,
returning HTTP 504 (`StatusCode::GATEWAY_TIMEOUT`) with `timeout_secs` on guest
command execution timeouts. Migrate `AppError` from a tuple struct to a named-field
struct with `AppError::new(status, error)` across `capsem-service` route handlers and
typed constructors (`AppError::vm_not_found`, `AppError::exec_timeout`,
`AppError::create_timeout`, and `AppError::with_code`).

In `sdk/python`:
- Parse `ErrorResponse` once in `HttpError.__init__` (`response` and `code`) and
  dispatch `VmNotFoundError`, `CreateTimeoutError`, and `ExecTimeoutError`
  (subclassing `CapsemTimeoutError(TimeoutError, CapsemError)`) from `_ERROR_DISPATCH`
  keyed by `models.ErrorCode`, with pickle round-trip support (`HttpError.__reduce__` /
  `__setstate__` and `Transport.__getstate__` dropping `_session`).
- Add host gateway URL and bearer token discovery (`discover_gateway`,
  `capsem_run_dir`, `Hypervisor.connect`). Note: `capsem_run_dir` is the one
  client-side path helper retained as an exception to relying on the server for
  path rules because `discover_gateway` resolves `~/.capsem/run/gateway.{port,token}`
  on the host before any gateway connection exists; guest file path sanitization
  is delegated entirely to the gateway (`POST/GET /vms/{id}/files/*`).
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