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

  1. If you saw this exact message, capture the shards Vec contents and report it as a sharder bug.
  2. 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).
  3. 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

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


AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16). Data as JSON: /api/errors/ecc878179104f4e3. Report an issue: GitHub.