cloudflare / cloudflare/workers-rs

[BUG] Queue: `Queue::send` default `content_type` changed `v8` → `json` (0.7 → 0.8) — silent cross-version message drops

Open
#1,012 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.