neondatabase/neon · critical

max_keys_per_list_response can't be 0

Error message

max_keys_per_list_response can't be 0

What it means

AzureBlobStorage::new converts the configured max_keys_per_list_response into a NonZeroU32 for the Azure list-blobs page size, because Azure rejects a zero 'maxresults'. NonZeroU32::new(0) returns None, which becomes this anyhow error during remote storage client construction -- i.e. the pageserver/safekeeper fails at config-load/startup.

Source

Thrown at libs/remote_storage/src/azure_blob.rs:107

                .context("trying to obtain Azure default credentials")?;
            StorageCredentials::token_credential(token_credential)
        };

        let builder = ClientBuilder::new(account, credentials)
            // we have an outer retry
            .retry(RetryOptions::none())
            // Customize transport to configure conneciton pooling
            .transport(TransportOptions::new(Self::reqwest_client(
                azure_config.conn_pool_size,
            )));

        let client = builder.container_client(azure_config.container_name.to_owned());

        let max_keys_per_list_response =
            if let Some(limit) = azure_config.max_keys_per_list_response {
                Some(
                    NonZeroU32::new(limit as u32)
                        .ok_or_else(|| anyhow::anyhow!("max_keys_per_list_response can't be 0"))?,
                )
            } else {
                None
            };

        Ok(AzureBlobStorage {
            client,
            container_name: azure_config.container_name.to_owned(),
            prefix_in_container: azure_config.prefix_in_container.to_owned(),
            max_keys_per_list_response,
            concurrency_limiter: ConcurrencyLimiter::new(azure_config.concurrency_limit.get()),
            timeout,
            small_timeout,
            /* BEGIN_HADRON */
            put_block_size_mb: azure_config.put_block_size_mb,
            /* END_HADRON */
        })
    }

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Set max_keys_per_list_response to a positive value (typical page sizes are 100-5000, Azure caps at 5000)
  2. Omit the setting entirely to use the None branch (SDK default), rather than writing 0
  3. Fix templating/code that encodes 'no limit' as 0 -- omit the field or use NonZeroU32 in your config struct
  4. Add a config lint/unit test asserting the value is None or >0 before startup

Example fix

# before (remote_storage config)
max_keys_per_list_response = 0

# after: either a positive page size
max_keys_per_list_response = 1000
# or omit the key entirely for the default
Defensive patterns

Strategy: validation

Validate before calling

// Validate at config-parse time so the process fails with a precise message
// instead of deep inside AzureBlobStorage::new:
if let Some(limit) = &config.max_keys_per_list_response {
    anyhow::ensure!(
        *limit > 0,
        "max_keys_per_list_response must be a positive u32 (1..=5000), got {limit}"
    );
}

Type guard

/// NonZero page-size guard; use it in the config struct to make 0 unrepresentable.
type MaxKeys = std::num::NonZeroU32;

fn as_max_keys(v: u32) -> Option<MaxKeys> {
    MaxKeys::new(v)
}

Try / catch

match remote_storage::GenericRemoteStorage::from_config(&remote_storage_config).await {
    Err(e) if format!("{e:#}").contains("max_keys_per_list_response can't be 0") => {
        // Config error, not transient: fix the config/secret and restart. Do not retry.
        anyhow::bail!("invalid remote_storage config: {e:#}; set max_keys_per_list_response > 0 or omit it");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Setting max_keys_per_list_response = 0 in the remote_storage Azure configuration (pageserver.toml / safekeeper config / env-provided config JSON). Construction of the Azure remote storage client aborts before serving any request.

Common situations: Config templating that maps 'unset/None' to 0 instead of omitting the key; copying a limit from another component where 0 means 'unlimited/default'; monitoring configs tuned down during load tests; hand-edited JSON configs.

Related errors


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