{"record":{"id":"0471d30a87452c34","repo":"risingwavelabs/risingwave","slug":"file-scan-tasks-must-be-some","errorCode":null,"errorMessage":"file_scan_tasks must be Some","messagePattern":"file_scan_tasks must be Some","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/batch/executors/src/executor/iceberg_scan.rs","lineNumber":101,"sourceCode":"            schema,\n            file_scan_tasks: Some(file_scan_tasks),\n            identity,\n            file_scan_metrics,\n            need_seq_num,\n            need_file_path_and_pos,\n            limit,\n        }\n    }\n\n    #[try_stream(ok = DataChunk, error = BatchError)]\n    async fn do_execute(mut self: Box<Self>) {\n        let table = self.iceberg_config.load_table().await?;\n        let data_types = self.schema.data_types();\n\n        let data_file_scan_tasks = match Option::take(&mut self.file_scan_tasks) {\n            Some(file_scan_tasks) => file_scan_tasks.into_tasks(),\n            None => {\n                bail!(\"file_scan_tasks must be Some\")\n            }\n        };\n        let mut remaining_limit = self\n            .limit\n            .map(|limit| usize::try_from(limit).unwrap_or(usize::MAX));\n\n        for data_file_scan_task in data_file_scan_tasks {\n            if matches!(remaining_limit, Some(0)) {\n                return Ok(());\n            }\n\n            #[for_await]\n            for chunk in scan_task_to_chunk_with_deletes(\n                table.clone(),\n                data_file_scan_task,\n                IcebergScanOpts {\n                    chunk_size: self.chunk_size,\n                    need_seq_num: self.need_seq_num,","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/batch/executors/src/executor/iceberg_scan.rs#L83-L119","documentation":"After decoding a seq-id range for a stream chunk, no data rows were found (`ops.is_empty()`). A chunk read must yield at least one row; an empty result means the range selection pointed at seq ids that contain nothing (or only non-row ops), indicating a bug in the reader's range computation rather than a legitimate empty read.","triggerScenarios":"Calling `deserialize_stream_chunk` with `[start_seq_id, end_seq_id)` whose decoded ops list is empty at the final check. Thrown at serde.rs:514.","commonSituations":"Read range where all entries are barriers or were truncated concurrently; stale start/end seq ids after truncation; writer skipped seq ids leaving gaps the reader assumed were populated.","solutions":["Verify the range selection logic only issues reads over non-empty, non-truncated seq-id spans.","Re-check truncation timing: if rows were truncated between range computation and read, re-select the range.","Inspect the KV store at start_seq_id/end_seq_id to confirm entries exist.","Ensure the writer never advances visible seq ids without writing rows."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// rust\n// confirm the range is non-empty and not yet truncated before decoding\nanyhow::ensure!(end_seq_id > start_seq_id, \"empty seq range [{}, {})\", start_seq_id, end_seq_id);\nanyhow::ensure!(start_seq_id > reader.current_truncate_seq_id(), \"range [{}, {}) already truncated\", start_seq_id, end_seq_id);","typeGuard":null,"tryCatchPattern":"// rust\nmatch reader.deserialize_stream_chunk(start, end, epoch).await {\n    Ok(chunk) => process(chunk),\n    Err(e) if e.to_string().contains(\"empty row\") => {\n        // re-select range from the store's current watermark before retrying\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Recompute read ranges from the reader's current watermark, not cached values.","Guard reads against concurrent truncation.","Never advance visible seq ids without a written row."],"tags":["log-store","deserialization","empty-read","range-selection"],"backgroundTag":"empty-result-set","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"}