linera-io/linera-protocol · error
capacity must be > 0
Error message
capacity must be > 0
What it means
QueryResponseCache::new wraps its per-chain LRU capacity in NonZeroUsize::new(capacity_per_chain).expect("capacity must be > 0"). A zero capacity is rejected because the underlying LRU (and the cache design) cannot function with zero slots. The value comes from the `linera service` --query-cache-size CLI argument (node_service.rs:1379), so passing 0 panics during service construction at startup.
Source
Thrown at linera-service/src/node_service.rs:1156
/// each insert carries the chain's `next_block_height` at query time.
/// If a newer block has since been processed, the insert is silently dropped.
struct QueryResponseCache {
chains: papaya::HashMap<ChainId, StdMutex<PerChainCache>>,
/// Chains for which we have registered a notification subscription.
subscribed: papaya::HashSet<ChainId>,
/// Sender half of the notification channel, used to subscribe new chains lazily.
notification_sender: StdMutex<Option<tokio::sync::mpsc::UnboundedSender<Notification>>>,
capacity_per_chain: std::num::NonZeroUsize,
}
impl QueryResponseCache {
fn new(capacity_per_chain: usize) -> Self {
Self {
chains: papaya::HashMap::new(),
subscribed: papaya::HashSet::new(),
notification_sender: StdMutex::new(None),
capacity_per_chain: std::num::NonZeroUsize::new(capacity_per_chain)
.expect("capacity must be > 0"),
}
}
/// Stores the notification sender (called once during startup).
fn set_notification_sender(&self, sender: tokio::sync::mpsc::UnboundedSender<Notification>) {
*self
.notification_sender
.lock()
.expect("sender mutex poisoned") = Some(sender);
}
/// Returns the notification sender, if set.
fn notification_sender(&self) -> Option<tokio::sync::mpsc::UnboundedSender<Notification>> {
self.notification_sender
.lock()
.expect("sender mutex poisoned")
.clone()
}View on GitHub (pinned to 6c226ddcb3)
Solutions
- Omit --query-cache-size entirely if you want no cache — the None default skips cache creation
- Otherwise pass a positive value, e.g. --query-cache-size 100 (the values used in tests)
- Fix wrapper scripts that coerce 'disabled'/'null' to 0 before invoking the CLI
- If size comes from a computed value, clamp it: max(1, computed)
Example fix
# before linera service --query-cache-size 0 # panics at startup # after (disable caching) linera service # after (keep cache) linera service --query-cache-size 100
Defensive patterns
Strategy: validation
Validate before calling
# Never pass 0; omit the flag to disable the cache.
CACHE_ARGS=()
if [ -n "$QUERY_CACHE_SIZE" ] && [ "$QUERY_CACHE_SIZE" != '0' ]; then
CACHE_ARGS=(--query-cache-size "$QUERY_CACHE_SIZE")
fi
linera service "${CACHE_ARGS[@]}" Prevention
- Treat 'disabled' as omitting the flag, not as 0
- Clamp computed sizes: SIZE=$(( SIZE > 0 ? SIZE : 1 )) or drop the flag
- Smoke-start the service with the final flag set in CI before deploying
When it happens
Trigger: Starting `linera service --query-cache-size 0` (or a config/script that sets the cache size to zero to 'disable' caching): NonZeroUsize::new(0) returns None and the expect aborts before the service starts listening.
Common situations: Operators trying to disable the query cache by setting its size to 0 (the flag is Option<usize>; disabling is done by omitting it); YAML/JSON-to-CLI translation scripts defaulting unset numeric values to 0; tuning scripts that compute size from a metric which can evaluate to 0.
Related errors
- Service requires a default chain in the wallet
- expected whitespace after 'query' keyword
- expected an operation name after 'query', e.g. 'query MyQuer
- The input has not matched: {input}
- empty
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/eaa8fd43946c4bf9.
Report an issue: GitHub.