{"record":{"id":"28d7c32df4765716","repo":"risingwavelabs/risingwave","slug":"bounded-compaction-head-sequence-is-older-than","errorCode":null,"errorMessage":"bounded compaction head sequence {} is older than boundary {}","messagePattern":"bounded compaction head sequence (.+?) is older than boundary (.+?)","errorType":"exception","errorClass":"HummockError","httpStatus":null,"severity":"error","filePath":"src/storage/src/hummock/compactor/iceberg_compaction/iceberg_compactor_runner.rs","lineNumber":972,"sourceCode":"        .load_table(&table_ident)\n        .await\n        .map_err(|e| HummockError::compaction_executor(e.as_report()))?;\n\n    if let Some(boundary) = max_file_sequence_number {\n        // An empty bounded plan is reported as `Drained`, so fail closed unless\n        // the loaded branch can prove that this fixed boundary is meaningful.\n        if table.metadata().format_version() < FormatVersion::V2 {\n            return Err(HummockError::compaction_executor(anyhow::anyhow!(\n                \"bounded compaction requires Iceberg format V2 or V3\"\n            )));\n        }\n        let head = table.metadata().snapshot_for_ref(&branch).ok_or_else(|| {\n            HummockError::compaction_executor(anyhow::anyhow!(\n                \"bounded compaction branch {branch} has no snapshot\"\n            ))\n        })?;\n        if head.sequence_number() < boundary {\n            return Err(HummockError::compaction_executor(anyhow::anyhow!(\n                \"bounded compaction head sequence {} is older than boundary {}\",\n                head.sequence_number(),\n                boundary\n            )));\n        }\n    }\n\n    let planning_config = build_task_planning_config(\n        compaction_kind,\n        &iceberg_config,\n        &config,\n        max_file_sequence_number,\n    )?;\n\n    let compaction_plans = CompactionPlanner::new(planning_config)\n        .plan_compaction_with_branch(&table, &branch)\n        .await\n        .map_err(|e| HummockError::compaction_executor(e.as_report()))?;","sourceCodeStart":954,"sourceCodeEnd":990,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/storage/src/hummock/compactor/iceberg_compaction/iceberg_compactor_runner.rs#L954-L990","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n// max_file_sequence_number = 1000 while head.sequence_number() = 42\n\n// after: fetch head sequence and clamp, or omit\n// max_file_sequence_number = null  // or <= head sequence","handlingStrategy":"validation","validationCode":"fn boundary_is_valid(table: &Table, branch: &str, boundary: i64) -> bool {\n    table.metadata().snapshot_for_ref(branch)\n        .map_or(false, |head| head.sequence_number() >= boundary)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["rust","iceberg","sequence-number","bounded-compaction","validation"],"backgroundTag":"argument-out-of-range","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}