neondatabase/neon · error

can't change stripe size from {} to {}

Error message

can't change stripe size from {} to {}

What it means

In `update_shards`, once a tenant is sharded its stripe size must stay constant across updates: the stripe size determines which shard owns each key, so changing it would silently remap every key. The check applies only when the current count is sharded (unsharded tenants may gain a stripe size when first split).

Source

Thrown at pageserver/client_grpc/src/client.rs:141

    ///
    /// TODO: verify that in-flight requests are allowed to complete, and that the old pools are
    /// properly spun down and dropped afterwards.
    pub fn update_shards(&self, shard_spec: ShardSpec) -> anyhow::Result<()> {
        // Validate the shard spec. We should really use `ArcSwap::rcu` for this, to avoid races
        // with concurrent updates, but that involves creating a new `Shards` on every attempt,
        // which spins up a bunch of Tokio tasks and such. These should already be checked elsewhere
        // in the stack, and if they're violated then we already have problems elsewhere, so a
        // best-effort but possibly-racy check is okay here.
        let old = self.shards.load_full();
        if shard_spec.count < old.count {
            return Err(anyhow!(
                "can't reduce shard count from {} to {}",
                old.count,
                shard_spec.count
            ));
        }
        if !old.count.is_unsharded() && shard_spec.stripe_size != old.stripe_size {
            return Err(anyhow!(
                "can't change stripe size from {} to {}",
                old.stripe_size.expect("always Some when sharded"),
                shard_spec.stripe_size.expect("always Some when sharded")
            ));
        }

        let shards = Shards::new(
            self.tenant_id,
            self.timeline_id,
            shard_spec,
            self.auth_token.clone(),
            self.compression,
        )?;
        self.shards.store(Arc::new(shards));
        Ok(())
    }

    /// Returns the total size of a database, as # of bytes.

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Pass the same stripe_size the tenant was originally sharded with (read it from the current shard map)
  2. If a different stripe size is truly required, that requires a new tenant/topology — not an in-place update
  3. Audit where shard_spec.stripe_size is computed and pin it to the controller's canonical value
  4. Add a precondition assertion in the caller comparing old and new specs before calling update_shards

Example fix

// before: sharded tenant (stripe 32768) updated with a different stripe
client.update_shards(ShardSpec::new(urls, Some(ShardStripeSize(16384)))?)?; // can't change stripe size

// after: keep the original stripe size
client.update_shards(ShardSpec::new(urls, Some(current_stripe_size))?)?;
Defensive patterns

Strategy: validation

Validate before calling

let old = client.shards();
if !old.count.is_unsharded() && shard_spec.stripe_size != old.stripe_size {
    anyhow::bail!(
        "stripe size is immutable once sharded (current {:?}, requested {:?})",
        old.stripe_size, shard_spec.stripe_size
    );
}
client.update_shards(shard_spec)?;

Prevention

When it happens

Trigger: Calling `update_shards` on an already-sharded tenant with a `ShardSpec` whose `stripe_size` differs from the currently loaded one — e.g. the controller's shard map was regenerated with a different stripe_size setting.

Common situations: Changing the sharding stripe-size configuration after tenants were sharded; controller state drift between the spec used at split time and a later reconciliation; merging specs from different sources.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/839cbd243c16dceb. Report an issue: GitHub.