cloudflare / cloudflare/workers-rs
[BUG] Queue: `Queue::send` default `content_type` changed `v8` → `json` (0.7 → 0.8) — silent cross-version message drops
- Dominant language
- Rust
- Stars
- 3.7k
- Forks
- 429
- Avg merge
- 20h 28m
- Merged PRs (30d)
- 7
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### What version of `workers-rs` are you using?
0.8.4
### What version of `wrangler` are you using?
4.100.0
### Describe the bug
### Summary
In `worker` 0.8.x, `Queue::send` defaults outgoing messages to `content_type = Json`. A consumer written against the 0.7.x default (which delivered a structured-clone **object** body) silently drops these messages: `send()` returns `Ok`, the consumer batch returns `Ok`, **no exception, no log, zero messages processed**. During a gradual upgrade — or when one queue is shared by services on different `worker` versions — this breaks invisibly.
### Environment
- `worker` **0.8.4** producer interoperating with a **0.7.x** consumer over a Cloudflare Queue.
- `wasm32-unknown-unknown`, Rust 1.96, `worker-build 0.8.4`.
### Where it comes from
`impl From for SendMessage` hard-codes the default:
```rust
// worker-0.8.4/src/queue.rs
impl From for SendMessage {
fn from(message: T) -> Self {
Self { message, options: Some(QueueSendOptions {
content_type: Some(QueueContentType::Json), // <-- default (was v8 in 0.7.x)
delay_seconds: None,
})}
}
}
```
A consumer expecting the old structured-clone object typically does:
```rust
let body = raw_message.body(); // JsValue
if !body.is_object() { continue; } // a `json`-typed body fails this → SILENTLY skipped
```
### Why this was painful
The wire format changed across a minor-version bump with no error and no obvious note. It took a long debugging session to find, because every layer reports success.
### Asks
- Call out the default-`content_type` change **prominently** in the CHANGELOG / a 0.7→0.8 migration note — it's a wire-format behavior change, not just an API change.
- Consider keeping the prior default, or at least making the default a clearly-documented, pinnable constant, with a note that **producer and consumer must agree** on `content_type`.
- Optionally: give consumers a way to detect a `content_type` their decoder didn't expect, instead of silently yielding a non-object body.
### Steps To Reproduce
1. Producer (0.8.x): `queue.send(MyMsg { .. }).await?` — default `content_type = Json`.
2. Consumer (0.7.x, or anything expecting a structured-clone object): `if !body.is_object() { continue }`.
3. Observe: producer `Ok`, consumer `Ok`, **0 messages processed**, no error anywhere.
**Workaround (producer side):**
```rust
queue.send(MessageBuilder::new(msg).content_type(QueueContentType::V8).build()).await?;
```
Related to #1013
Contributor guide
Assessment
This issue has not been assessed yet.