risingwavelabs/risingwave · error · HummockError
bounded compaction head sequence {} is older than boundary {
Error message
bounded compaction head sequence {} is older than boundary {} What it means
During bounded compaction validation, the head snapshot's sequence number is compared with the requested max_file_sequence_number boundary. If head.sequence_number() < boundary, the boundary exceeds anything the table has ever written, so it is provably meaningless and the runner rejects the task rather than producing an empty/incorrect bounded plan.
Source
Thrown at src/storage/src/hummock/compactor/iceberg_compaction/iceberg_compactor_runner.rs:972
.load_table(&table_ident)
.await
.map_err(|e| HummockError::compaction_executor(e.as_report()))?;
if let Some(boundary) = max_file_sequence_number {
// An empty bounded plan is reported as `Drained`, so fail closed unless
// the loaded branch can prove that this fixed boundary is meaningful.
if table.metadata().format_version() < FormatVersion::V2 {
return Err(HummockError::compaction_executor(anyhow::anyhow!(
"bounded compaction requires Iceberg format V2 or V3"
)));
}
let head = table.metadata().snapshot_for_ref(&branch).ok_or_else(|| {
HummockError::compaction_executor(anyhow::anyhow!(
"bounded compaction branch {branch} has no snapshot"
))
})?;
if head.sequence_number() < boundary {
return Err(HummockError::compaction_executor(anyhow::anyhow!(
"bounded compaction head sequence {} is older than boundary {}",
head.sequence_number(),
boundary
)));
}
}
let planning_config = build_task_planning_config(
compaction_kind,
&iceberg_config,
&config,
max_file_sequence_number,
)?;
let compaction_plans = CompactionPlanner::new(planning_config)
.plan_compaction_with_branch(&table, &branch)
.await
.map_err(|e| HummockError::compaction_executor(e.as_report()))?;View on GitHub (pinned to 6469eb736d)
Solutions
- Re-read the current head snapshot's sequence number for the branch and send a boundary <= that value (or omit max_file_sequence_number entirely).
- If the table was recreated, refresh any persisted boundary state in the coordinating system to match the new table's sequence space.
- Use an unbounded compaction task when you just want a full/normal compaction instead of tracking sequence numbers.
- Confirm the request targets the intended table/branch — boundaries from a different branch can exceed this branch's sequence numbers.
Example fix
// before // max_file_sequence_number = 1000 while head.sequence_number() = 42 // after: fetch head sequence and clamp, or omit // max_file_sequence_number = null // or <= head sequence
Defensive patterns
Strategy: validation
Validate before calling
fn boundary_is_valid(table: &Table, branch: &str, boundary: i64) -> bool {
table.metadata().snapshot_for_ref(branch)
.map_or(false, |head| head.sequence_number() >= boundary)
} Prevention
- Always fetch the head sequence number from the live table metadata, never cache boundaries across table recreation.
- Omit max_file_sequence_number unless incremental bounded semantics are truly needed.
- Namespace persisted boundaries by table id + branch to avoid cross-table reuse.
When it happens
Trigger: create_task_execution with max_file_sequence_number > head snapshot's sequence_number for the target branch — the caller supplied a stale or fabricated boundary larger than any committed sequence number.
Common situations: A client cached sequence numbers from a different table/branch and sent them to this one; a table was recreated (sequence numbers reset) while the coordinator still held old boundaries; a manual/ops script hardcodes a boundary value.
Related errors
- 'copy-on-write' mode is not supported for append-only iceber
- order_key must not be empty
- System column `{}` is not allowed in order_key
- bounded compaction requires Iceberg format V2 or V3
- bounded compaction branch {branch} has no snapshot
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/28d7c32df4765716.
Report an issue: GitHub.