Skip to content

fix(rest): return immediately on volume create, drop synchronous wait - #1196

Draft
G4614 wants to merge 3 commits into
boxlite-ai:mainfrom
G4614:fix/volume-create-async
Draft

fix(rest): return immediately on volume create, drop synchronous wait#1196
G4614 wants to merge 3 commits into
boxlite-ai:mainfrom
G4614:fix/volume-create-async

Conversation

@G4614

@G4614 G4614 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #1191 (merged). create blocked up to 30s polling for the volume to become ready via VolumeService.waitForReady, risking client/gateway timeouts around that same window for what is an inherently async operation — bucket provisioning runs on a 5s reconciler tick (VolumeManager.processPendingVolumes), fully decoupled from the request.

API: create now returns 202 with state=pending_create as soon as the row exists, matching what the pre-existing classic VolumeController (apps/api/src/box/controllers/volume.controller.ts) already did — it never waited either. Removes VolumeService.waitForReady, now unused.

Client-side wait: the Rust core's VolumeInfo/VolumeResponse never carried state at all, so there was no way for any caller to observe readiness short of re-deserializing raw JSON. Adds a VolumeState enum (mirroring the API's) plus state/error_reason fields, and VolumeHandle::wait_until_ready — a client-side poll loop mirroring ComputerBox/SkillBox's waitUntilReady in the Node SDK — that blocks until ready/error/timeout without the server holding a connection open. Wired into the CLI: boxlite volume create --wait [--wait-timeout secs] restores the old synchronous-feeling UX for callers who want it; volume ls/get now show a STATE column.

Callers who don't use --wait learn readiness via GET /volumes/{id} (always authoritative, no external dependency) or the existing volume.state.updated webhook if their org has one configured (best-effort, opt-in — not a delivery guarantee on its own).

Test plan:

  • yarn nx run api:build / yarn nx run api:test -- --testPathPatterns=volume — 4 suites, 23 tests, all pass
  • cargo check -p boxlite --features rest / cargo build -p boxlite-cli — clean
  • cargo test -p boxlite-cli volume:: — 2 new/updated unit tests pass
  • cargo fmt --all -- --check — clean
  • Manual E2E against a local isolated API+Postgres+MinIO stack: boxlite volume create returns in ~0.3s with state=pending_create; boxlite volume create --wait blocks ~2s and returns once state=ready; boxlite volume ls shows the new STATE column throughout

POST /volumes blocked up to 30s polling for the volume to become ready,
risking client/gateway timeouts around that same window for what is an
inherently async operation (bucket provisioning runs on a 5s reconciler
tick, decoupled from the request). Return 202 with state=pending_create
as soon as the row exists, matching the pre-existing classic
VolumeController's behavior (apps/api/src/box/controllers/volume.controller.ts)
which never waited either. Callers learn readiness via GET or the
existing volume.state.updated webhook (apps/api/src/box/subscribers/volume.subscriber.ts),
not by polling inside a single held-open request.

Removes VolumeService.waitForReady, now unused.
@G4614
G4614 requested a review from a team as a code owner August 11, 2026 08:13
@boxlite-agent

boxlite-agent Bot commented Aug 11, 2026

Copy link
Copy Markdown

📦 BoxLite review — couldn't complete

claude exited 1

stdout:
{"is_error":true,"duration_api_ms":0,"num_turns":1,"stop_reason":"stop_sequence","session_id":"36d7b107-db9e-423d-b78d-8c2bae9518ca","total_cost_usd":0,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{},"permission_denials":[],"terminal_reason":"api_error","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subtype":"success","api_error_status":403,"result":"Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access","type":"result","duration_ms":272,"uuid":"d1a92d18-07bd-44c9-a956-8ce787e2dd97"}

stderr:
<empty>

powered by BoxLite

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbc19c37-53b7-4a28-9c1e-20cd24343797

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Volume creation no longer waits for provisioning readiness. The controller returns HTTP 202 with the current volume state. The service polling method and related tests are removed. The OpenAPI contract documents asynchronous creation and removes timeout responses.

Changes

Asynchronous volume creation

Layer / File(s) Summary
Remove readiness polling
apps/api/src/box/services/volume.service.ts, apps/api/src/box/services/volume.service.spec.ts
Removes VolumeService.waitForReady, its polling logic, exception handling, imports, and tests.
Return accepted creation responses
apps/api/src/boxlite-rest/boxlite-volume.controller.ts, apps/api/src/boxlite-rest/boxlite-volume.controller.spec.ts, openapi/box.openapi.yaml
Volume creation returns HTTP 202 immediately. Tests remove readiness-wait expectations. The OpenAPI contract documents pending_create and removes the synchronous 201 and timeout responses.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BoxliteVolumeController
  participant VolumeService
  Client->>BoxliteVolumeController: POST /volumes
  BoxliteVolumeController->>VolumeService: create()
  VolumeService-->>BoxliteVolumeController: newly created volume
  BoxliteVolumeController-->>Client: 202 Accepted with pending_create state
Loading

Possibly related PRs

Suggested reviewers: dorianzheng

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the behavior change and verification steps but omits the required explicit call graph and template section structure. Add the required Before and After call graph, plus explicit Changes and How to verify sections using the repository template.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary change: REST volume creation returns immediately without synchronous readiness polling.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@G4614
G4614 marked this pull request as draft August 11, 2026 08:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openapi/box.openapi.yaml`:
- Around line 195-204: Update the createVolume controller to return HTTP 202,
matching the documented asynchronous creation contract and the OpenAPI response.
Regenerate the TypeScript and Go client artifacts from the updated
specification, then adjust related tests and documentation to expect 202.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ef3fa219-b516-4dd7-962e-0ee876df8949

📥 Commits

Reviewing files that changed from the base of the PR and between 596400e and d287f3c.

📒 Files selected for processing (5)
  • apps/api/src/box/services/volume.service.spec.ts
  • apps/api/src/box/services/volume.service.ts
  • apps/api/src/boxlite-rest/boxlite-volume.controller.spec.ts
  • apps/api/src/boxlite-rest/boxlite-volume.controller.ts
  • openapi/box.openapi.yaml
💤 Files with no reviewable changes (1)
  • apps/api/src/boxlite-rest/boxlite-volume.controller.spec.ts

Comment thread openapi/box.openapi.yaml
…t/CLI

The REST volume create endpoint returns as soon as the volume is accepted
(state=pending_create); provisioning happens asynchronously server-side.
The Rust core's VolumeInfo/VolumeResponse never carried state at all, so
there was no way for a client to observe this. Adds VolumeState (mirroring
the API's enum) plus state/error_reason fields, and VolumeHandle::wait_until_ready
— a client-side poll loop (mirrors ComputerBox/SkillBox's waitUntilReady in
the Node SDK) that blocks until ready/error/timeout without the server
holding a connection open.

Wires it into the CLI: `boxlite volume create --wait [--wait-timeout secs]`
restores the old synchronous-feeling UX for callers who want it, and
`volume ls`/`get` now show a STATE column.
PyVolumeInfo/JsVolumeInfo only ever carried id/created_at/size_bytes,
matching the pre-fix VolumeInfo — SDK callers had no way to see whether
a volume they just created (state=pending_create) had actually become
ready, short of raw-decoding the wire response themselves.

Adds state (string, matching the API's snake_case wire values) and
error_reason, and exposes VolumeHandle::wait_until_ready (added earlier
in this PR for the Rust core/CLI) as create()/wait_until_ready() on both
PyVolumeHandle and JsVolumeHandle, mirroring create/list/get/remove.
DorianZheng pushed a commit that referenced this pull request Aug 11, 2026
~~Expose existing S3 managed volumes through the BoxLite REST API and
carry volume mounts into REST-created boxes.~~

**Rebuilt on top of main** now that this PR's backend content has been
split out and merged separately:
- #1191 — volume REST CRUD (`/v1/volumes` create/list/get/delete)
- #1192 — REST box-create volume mount wiring (`VolumeSpec.source`)
- (follow-up, still open) #1196 — makes `create` return immediately
instead of blocking, adds client-side `wait_until_ready`

What's left here is the remaining, still-unmerged piece: **CLI +
Node/Python SDK support for attaching a managed volume to a box**, on
top of the server-side contract #1191/#1192 already shipped.

- CLI: `--mount src=volume://<id>,target=<path>` on `create`/`run`
- Node/Python SDKs: accept a scheme-qualified volume source
(`volume://<id>`) alongside the existing host-path volume option

Test plan:
- `cargo build -p boxlite-cli` / `cargo test -p boxlite-cli --bin
boxlite volume` — 30 tests, all pass
- `cargo check -p boxlite-node` / `cargo check -p boxlite-python` —
clean
- `cargo fmt --all -- --check` — clean
- Fixed a pre-existing test bug found during verification:
`test_volume_flags_managed_volume_rejects_missing_id`'s fixture never
reached the code path it meant to exercise (see commit message for
detail)
/// enum (`apps/api/src/box/enums/volume-state.enum.ts`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VolumeState {

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.

3 states is good enough. and consistent with BoxState

/// timeouts for what both `boxlite`'s runner and API treat as inherently
/// async). Mirrors `ComputerBox`/`SkillBox`'s `waitUntilReady` in the
/// Node SDK (`sdks/node/lib/computerbox.ts`, `skillbox.ts`).
pub async fn wait_until_ready(&self, id: &str, timeout: Duration) -> BoxliteResult<VolumeInfo> {

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.

remove

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