risingwavelabs/risingwave · error
duplicate vnode {:?}. request vnode: {:?}, prev vnode: {:?}.
Error message
duplicate vnode {:?}. request vnode: {:?}, prev vnode: {:?}. pending request: {:?}, request: {:?} What it means
Returned by `CoordinatorWorkerContext::add_new_request` when a sink coordinator request's vnode bitmap overlaps vnodes already covered by the committed bitmap for the current epoch. The overlap bits (`check_bitmap.count_ones() > 0`) mean two requests claim the same vnode, which would corrupt sink coordination state, so the request is rejected with a diagnostic dump.
Source
Thrown at src/meta/src/manager/sink_coordination/coordinator_worker.rs:104
impl<R> AligningRequests<R> {
fn add_new_request(
&mut self,
handle_id: HandleId,
request: R,
vnode_bitmap: &Bitmap,
) -> anyhow::Result<()>
where
R: Debug,
{
let committed_bitmap = self
.committed_bitmap
.get_or_insert_with(|| Bitmap::zeros(vnode_bitmap.len()));
assert_eq!(committed_bitmap.len(), vnode_bitmap.len());
let check_bitmap = (&*committed_bitmap) & vnode_bitmap;
if check_bitmap.count_ones() > 0 {
return Err(anyhow!(
"duplicate vnode {:?}. request vnode: {:?}, prev vnode: {:?}. pending request: {:?}, request: {:?}",
check_bitmap.iter_ones().collect_vec(),
vnode_bitmap,
committed_bitmap,
self.requests,
request
));
}
*committed_bitmap |= vnode_bitmap;
self.requests.push(request);
assert!(self.handle_ids.insert(handle_id));
Ok(())
}
fn aligned(&self) -> bool {
self.committed_bitmap.as_ref().is_some_and(|b| b.all())
}
}View on GitHub (pinned to 6469eb736d)
Solutions
- Check whether the client/worker is retrying an already-acked request; make request submission idempotent by keying on request id/epoch.
- Compare the `request vnode` vs `prev vnode` lists in the message to find the overlapping vnodes and trace which worker owns them.
- Verify vnode assignment logic on scaling/parallelism changes so a distribution is applied exactly once.
- If this is a stale duplicate, drop the newer request rather than re-registering.
Defensive patterns
Strategy: validation
Validate before calling
// detect overlap before submitting a coordination request
let overlap = (&committed_bitmap) & &vnode_bitmap;
if overlap.count_ones() > 0 { return Err(anyhow!("request overlaps committed vnodes: {:?}", overlap.iter_ones().collect_vec())); } Try / catch
match worker.add_new_request(request).await {
Err(e) if e.to_string().starts_with("duplicate vnode") => {
// treat as duplicate: drop the request and wait for ack of the original
}
other => other?,
} Prevention
- Make coordinator request submission idempotent (keyed by request id/epoch)
- Avoid re-registering vnode distributions after worker restarts without clearing old state
- Review vnode assignment on parallelism changes to guarantee single ownership
When it happens
Trigger: A sink coordinator worker re-sends a request for vnodes it already committed (e.g. retry after a lost/late ack), or two workers simultaneously claim the same vnode range for the same sink and epoch.
Common situations: Network retries duplicating coordinator requests; worker restart re-registering the same vnode distribution; a bug in vnode assignment (e.g. scaling events applying a stale distribution).
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- replace sink must not use snapshot backfill
- old sink job {} not found in barrier state
- Dropping sink into table is not allowed for unmigrated table
- replacement sink catalog requires a sink job
- old sink {} does not match replacement sink {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/fa9f3f6408bd5002.
Report an issue: GitHub.