risingwavelabs/risingwave · error

failed to find handle {} when acknowledging the commit for e

Error message

failed to find handle {} when acknowledging the commit for epoch {}

What it means

`ack_commit` looks up each supplied `HandleId` in `writer_handles` to acknowledge a committed epoch; this error means one of the ids was not found. As with error 1772, this signals stale or unregistered handle ids reaching the manager — the commit was for a sink writer the coordinator no longer tracks.

Source

Thrown at src/meta/src/manager/sink_coordination/coordinator_worker.rs:327

                .map_err(|_| {
                    anyhow!(
                        "failed to ack aligned initial epoch {:?} for handle {}",
                        aligned_initial_epoch,
                        handle_id
                    )
                })?;
        }
        Ok(())
    }

    fn ack_commit(
        &mut self,
        epoch: u64,
        handle_ids: impl IntoIterator<Item = HandleId>,
    ) -> anyhow::Result<()> {
        for handle_id in handle_ids {
            let handle = self.writer_handles.get_mut(&handle_id).ok_or_else(|| {
                anyhow!(
                    "failed to find handle {} when acknowledging the commit for epoch {}",
                    handle_id,
                    epoch
                )
            })?;
            handle.ack_commit(epoch).map_err(|_| {
                anyhow!(
                    "failed to acknowledge the commit for epoch {} on handle {}",
                    epoch,
                    handle_id
                )
            })?;
        }
        Ok(())
    }

    async fn next_request_inner(
        writer_handles: &mut HashMap<HandleId, SinkWriterCoordinationHandle>,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Filter the ids passed to `ack_commit` against currently registered handles before calling it.
  2. Track per-handle pending epochs so commits for removed handles are ignored rather than erroring.
  3. Serialize stop/parallelism-change operations against commit acknowledgment to avoid the race.
  4. If the sink is recovering, re-register handles before acknowledging commits.

Example fix

// before
manager.ack_commit(epoch, all_pending_ids)?;

// after
let live: HashSet<HandleId> = manager.registered_handle_ids().collect();
manager.ack_commit(epoch, all_pending_ids.into_iter().filter(|id| live.contains(id)))?;
Defensive patterns

Strategy: validation

Validate before calling

// Drop ids for handles that no longer exist before acking
let live: HashSet<_> = manager.registered_handle_ids().collect();
let ackable: Vec<_> = pending_ids.into_iter().filter(|id| live.contains(id)).collect();

Type guard

fn handle_exists(mgr: &CoordinationHandleManager, id: &HandleId) -> bool {
    mgr.registered_handle_ids().contains(id)
}

Try / catch

if let Err(e) = manager.ack_commit(epoch, ids) {
    if e.to_string().contains("failed to find handle") {
        warn!(epoch, "commit for removed handle ignored");
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: A commit request was accepted from a handle that was subsequently removed (e.g. after `Stop` or during `alter_parallelisms`), and its id is later passed to `ack_commit`; or the caller passes ids from persisted metadata that do not match live registrations.

Common situations: Parallelism change concurrently dropping writers while their epochs are being committed; recovery from a snapshot where handle ids were reassigned; double commit acknowledgments after failover.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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