Skip to content

Resolve #485: demonstrate + regression-test Send/Sync for R2 Bucket in axum handlers - #1055

Open
NAVEENKUMARKR777 wants to merge 1 commit into
cloudflare:mainfrom
NAVEENKUMARKR777:fix/r2-bucket-send-axum
Open

Resolve #485: demonstrate + regression-test Send/Sync for R2 Bucket in axum handlers#1055
NAVEENKUMARKR777 wants to merge 1 commit into
cloudflare:mainfrom
NAVEENKUMARKR777:fix/r2-bucket-send-axum

Conversation

@NAVEENKUMARKR777

Copy link
Copy Markdown

Summary

Closes the reproduction described in #485, where using Bucket (an R2 binding) inside an axum handler fails to compile with:

error[E0277]: `Rc<RefCell<wasm_bindgen_futures::Inner>>` cannot be sent between threads safely

Investigating on current main, I found this is actually resolvable today, but the fix was never demonstrated or tested end-to-end:

  • Bucket (and the other R2 handle types: Object, Objects, MultipartUpload, UploadedPart) are already Send + Sync — no manual unsafe impl needed, because wasm_bindgen::JsValue itself is unconditionally Send + Sync outside of atomics builds, and these types are thin JsValue wrappers. So storing a Bucket in axum router state (AppState) already works fine.
  • The remaining blocker in the original repro is that Bucket::list().execute() (and similarly other R2 methods) internally .awaits a JsFuture, whose Rc<RefCell<Inner>> state isn't Send. That makes the handler's generated future !Send, which axum's Handler trait rejects.
  • The existing #[worker::send] macro (added after this issue was filed) fixes exactly this: it wraps the handler body in SendFuture, which is unconditionally (and safely, since Workers are single-threaded) marked Send, erasing the bound regardless of what's captured inside.

I verified this by reproducing the exact repro from the issue (axum State<AppState> extractor + #[debug_handler] + Bucket::list().execute().await): it fails with the same error without #[worker::send], and compiles cleanly with it added.

Changes

  • examples/axum/src/lib.rs: adds a Bucket field to AppState and a list_bucket handler demonstrating the working #[worker::send] pattern, with a doc comment explaining why it's needed and linking back to [BUG] Rc<RefCell<wasm_bindgen_futures::Inner>> cannot be sent between threads safely #485 for future readers who hit the same wall.
  • worker/src/r2/mod.rs: adds a compile-time Send/Sync regression test (mirroring the existing pattern in worker/src/email.rs) for Bucket, Object, Objects, MultipartUpload, and UploadedPart, so a future change (e.g. to worker-sys or a wasm-bindgen upgrade) that accidentally breaks this doesn't go unnoticed.

Test plan

  • cargo check --target wasm32-unknown-unknown -p worker --tests passes, exercising the new send_check module.
  • Reproduced the original issue's compile error with the handler without #[worker::send] (confirms root cause).
  • cargo check -p axum-on-workers --target wasm32-unknown-unknown compiles cleanly with #[worker::send] added (confirms the fix).
  • cargo fmt / cargo clippy -p axum-on-workers --no-deps clean on the touched code (pre-existing warnings elsewhere in the example are unrelated).

🤖 Generated with Claude Code

Closes the long-standing repro in cloudflare#485: `Bucket` (and the other R2
handle types) are already `Send + Sync` today because `JsValue` itself
is unconditionally `Send + Sync` on non-atomics wasm32 builds, so it's
safe to hold a `Bucket` in axum router state. The remaining piece of
the original error was that `Bucket::list().execute()` awaits a
`JsFuture` internally, which makes the handler's generated future
`!Send` unless the handler body is wrapped with `#[worker::send]`.

Adds a compile-time Send/Sync check for the R2 handle types to guard
against regressions, and extends the axum example with a working
`list_bucket` handler reproducing and resolving the original issue.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

1 participant