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

  1. Check whether the client/worker is retrying an already-acked request; make request submission idempotent by keying on request id/epoch.
  2. Compare the `request vnode` vs `prev vnode` lists in the message to find the overlapping vnodes and trace which worker owns them.
  3. Verify vnode assignment logic on scaling/parallelism changes so a distribution is applied exactly once.
  4. 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

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


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/fa9f3f6408bd5002. Report an issue: GitHub.