influxdata/influxdb · warning
mapped to out-of-bounds shard
Error message
mapped to out-of-bounds shard
What it means
RoundRobin::next() takes a thread-local counter, reduces it with counter % self.shards.len(), and indexes the shard list. Because the modulo already guarantees idx < len, the .expect('mapped to out-of-bounds shard') is a defensive invariant guard that should be unreachable. Note that if the shard list is empty, this method panics earlier at counter % 0 ('attempt to calculate the remainder with a divisor of zero'), not at this expect.
Source
Thrown at core/sharder/src/round_robin.rs:47
shards: shards.into_iter().collect(),
}
}
/// Return the next `T` to be used.
pub fn next(&self) -> &T {
// Grab and increment the current counter.
let counter = COUNTER.with(|cell| {
let mut cell = cell.borrow_mut();
let new_value = cell.wrapping_add(1);
*cell = new_value;
new_value
});
// Reduce it to the range of [0, N) where N is the number of shards in
// this sharder.
let idx = counter % self.shards.len();
self.shards.get(idx).expect("mapped to out-of-bounds shard")
}
}
impl<T, U> Sharder<U> for RoundRobin<Arc<T>>
where
T: Send + Sync + Debug,
U: Send + Sync + Debug,
{
type Item = Arc<T>;
fn shard(
&self,
_table: &str,
_namespace: &data_types::NamespaceName<'_>,
_payload: &U,
) -> Self::Item {
Arc::clone(self.next())
}View on GitHub (pinned to d28e26e048)
Solutions
- If you saw this exact message, capture the shards Vec contents and report it as a sharder bug.
- For the adjacent real failure: never construct RoundRobin with an empty iterator — assert at construction in your own wrapper (RoundRobin::new itself does not check).
- Add a non-empty assert or return-Err wrapper around RoundRobin::new at the topology-loading boundary so a zero-node cluster fails at startup with a clear message.
Example fix
// before let sharder = RoundRobin::new(discovered_nodes); // may be empty => panic on first next() // after assert!(!nodes.is_empty(), "cannot shard to zero nodes"); let sharder = RoundRobin::new(discovered_nodes);
Defensive patterns
Strategy: validation
Validate before calling
// RoundRobin::new does not check emptiness — check before constructing: let shards: Vec<_> = endpoints.into_iter().collect(); assert!(!shards.is_empty(), "RoundRobin requires at least one shard"); let sharder = RoundRobin::new(shards);
Type guard
fn non_empty<T>(v: &[T]) -> bool { !v.is_empty() } Prevention
- Guard the node list from discovery/health checks so a zero-node snapshot never reaches RoundRobin::new.
- Fail at startup with a descriptive message instead of at first request (modulo-by-zero panic).
- Log the shard count when constructing sharders so degenerate configurations are visible.
When it happens
Trigger: Calling RoundRobin::next() (directly or via the Sharder::shard impl that ignores table/namespace). With a non-empty shards Vec the expect cannot fire; an empty Vec panics at the modulo first. It only fires if the shards field is corrupted between the len() read and the .get(), which is effectively impossible in safe Rust.
Common situations: Practically unseen. The related real-world failure is constructing RoundRobin::new(empty_iterator) and then calling next(), which panics in the modulo — typically from a discovery/health-check layer passing zero healthy nodes into the sharder.
Related errors
- sharder mapped input to non-existant bucket
- num_columns_in_parallel should be above zero
- If you call `drop_last_value`, the tag buffer must contain a
- If we can remove a value from the interned strings, we must
- must have offset
AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16).
Data as JSON: /api/errors/ecc878179104f4e3.
Report an issue: GitHub.