influxdata/influxdb · error · Error
row range .. is out of bounds for a chunk of rows
Error message
row range {start}..{end} is out of bounds for a chunk of {row_count} rows What it means
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.
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.
Example fix
// before
let rows = chunk.read(start..start + PAGE_SIZE)?;
// after
let end = (start + PAGE_SIZE).min(chunk.row_count());
let rows = if start < end { chunk.read(start..end)? } else { &[] }; Defensive patterns
Strategy: validation
Validate before calling
let row_count = chunk.row_count();
let start = start.min(row_count);
let end = end.min(row_count);
if start >= end { return Ok(Vec::new()); } // empty range is valid, skip the read
chunk.read(start..end) Type guard
fn is_valid_range(range: std::ops::Range<usize>, row_count: usize) -> bool {
range.start < range.end && range.end <= row_count
} Try / catch
match chunk.read(range.clone()) {
Ok(rows) => process(rows),
Err(Error::RowRangeOutOfBounds { start, end, row_count }) => {
log::warn("range {start}..{end} exceeds {row_count} rows; clamping");
process(chunk.read(start.min(row_count)..end.min(row_count))?)
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Error creating record batch
- Field not found in table buffer
- Invalid Flatbuffer
- Message with header of type dictionary batch could not…
- no FlightData containing a Schema returned
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/aaa94e3bf453e009.
Report an issue: GitHub.
Appendix: source
Thrown at influxdb3_write/src/write_buffer/table_buffer.rs:35
use schema::{InfluxColumnType, InfluxFieldType, Schema, SchemaBuilder};
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::mem::size_of;
use std::ops::Range;
use std::sync::Arc;
use thiserror::Error;
use crate::ChunkFilter;
#[derive(Debug, Error)]
pub enum Error {
#[error("Field not found in table buffer: {0}")]
FieldNotFound(String),
#[error("Error creating record batch: {0}")]
RecordBatchError(#[from] arrow::error::ArrowError),
#[error("row range {start}..{end} is out of bounds for a chunk of {row_count} rows")]
RowRangeOutOfBounds {
start: usize,
end: usize,
row_count: usize,
},
}
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Default)]
pub struct TableBuffer {
chunk_time_to_chunks: BTreeMap<i64, ChunkTimeBuffer>,
snapshotting_chunks: Vec<SnapshotChunk>,
}
/// All buffered chunks for one chunk_time (gen1 window) of a table, plus the
/// overwrite index spanning them. A chunk_time usually holds one chunk; a
/// var-column overflow splits it into several, and hoisting the index hereView on GitHub (pinned to 06200ef96b)