{"record":{"id":"aaa94e3bf453e009","repo":"influxdata/influxdb","slug":"row-range-start-end-is-out-of-bounds-for-a-chunk-of-row","errorCode":null,"errorMessage":"row range {start}..{end} is out of bounds for a chunk of {row_count} rows","messagePattern":"row range (.+?)\\.\\.(.+?) is out of bounds for a chunk of (.+?) rows","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"influxdb3_write/src/write_buffer/table_buffer.rs","lineNumber":35,"sourceCode":"use schema::{InfluxColumnType, InfluxFieldType, Schema, SchemaBuilder};\nuse std::collections::BTreeMap;\nuse std::collections::btree_map::Entry;\nuse std::mem::size_of;\nuse std::ops::Range;\nuse std::sync::Arc;\nuse thiserror::Error;\n\nuse crate::ChunkFilter;\n\n#[derive(Debug, Error)]\npub enum Error {\n    #[error(\"Field not found in table buffer: {0}\")]\n    FieldNotFound(String),\n\n    #[error(\"Error creating record batch: {0}\")]\n    RecordBatchError(#[from] arrow::error::ArrowError),\n\n    #[error(\"row range {start}..{end} is out of bounds for a chunk of {row_count} rows\")]\n    RowRangeOutOfBounds {\n        start: usize,\n        end: usize,\n        row_count: usize,\n    },\n}\n\npub(crate) type Result<T, E = Error> = std::result::Result<T, E>;\n\n#[derive(Default)]\npub struct TableBuffer {\n    chunk_time_to_chunks: BTreeMap<i64, ChunkTimeBuffer>,\n    snapshotting_chunks: Vec<SnapshotChunk>,\n}\n\n/// All buffered chunks for one chunk_time (gen1 window) of a table, plus the\n/// overwrite index spanning them. A chunk_time usually holds one chunk; a\n/// var-column overflow splits it into several, and hoisting the index here","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_write/src/write_buffer/table_buffer.rs#L17-L53","documentation":"Thrown when a caller slices a buffered chunk with a row range (start..end) that exceeds the chunk's actual row_count. The buffer validates the requested bounds before reading rows and reports the offending start, end, and the chunk's size. It protects against out-of-bounds reads on Arrow record data.","triggerScenarios":"Calling a chunk-read/range API on TableBuffer (e.g. reading rows start..end from a Chunk) where end > row_count or start > end / start > row_count — typically from computed pagination offsets, a stale cached row count, or a caller assuming a different chunk size than the buffer holds.","commonSituations":"Paginating query results with a fixed page size larger than the final chunk; cached chunk metadata (row_count) going stale after a compaction/deduplication pass; off-by-one in end = start + page_size without clamping to row_count; concurrent truncation of the buffer while a read is computed.","solutions":["Clamp the requested range to the chunk's row_count before reading: end = end.min(row_count), and skip the read when start >= end.","Fetch row_count from the live chunk/chunk summary rather than a cached or assumed value.","Fix pagination loops to use min(start + page_size, row_count) for the last page.","If ranges come from query planning, recompute bounds against the current buffer state under the same lock/epoch that serves the read."],"exampleFix":"// before\nlet rows = chunk.read(start..start + PAGE_SIZE)?;\n\n// after\nlet end = (start + PAGE_SIZE).min(chunk.row_count());\nlet rows = if start < end { chunk.read(start..end)? } else { &[] };","handlingStrategy":"validation","validationCode":"let row_count = chunk.row_count();\nlet start = start.min(row_count);\nlet end = end.min(row_count);\nif start >= end { return Ok(Vec::new()); } // empty range is valid, skip the read\nchunk.read(start..end)","typeGuard":"fn is_valid_range(range: std::ops::Range<usize>, row_count: usize) -> bool {\n    range.start < range.end && range.end <= row_count\n}","tryCatchPattern":"match chunk.read(range.clone()) {\n    Ok(rows) => process(rows),\n    Err(Error::RowRangeOutOfBounds { start, end, row_count }) => {\n        log::warn(\"range {start}..{end} exceeds {row_count} rows; clamping\");\n        process(chunk.read(start.min(row_count)..end.min(row_count))?)\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always clamp pagination ranges with .min(row_count) before issuing a chunk read.","Read row_count from the chunk itself, never from cached metadata that can go stale after compaction.","Treat empty ranges (start >= end) as a valid no-op instead of an error path."],"tags":["rust","arrow","bounds","pagination"],"backgroundTag":"index-out-of-bounds","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}