linera-io/linera-protocol · error · async_graphql::Error

count exceeds u32

Error message

count exceeds u32

What it means

The GraphQL schema generated for `MapView` exposes a derived `count` field typed u32. The true number of keys is a `usize` produced by `iterative_count`; if it exceeds `u32::MAX` the `u32::try_from` fails and the resolver returns 'count exceeds u32'. This can only happen with over four billion map entries — an overflow guard, rarely seen in practice.

Source

Thrown at linera-views/src/views/map_view.rs:2282

            + Clone
            + Send
            + Sync
            + 'static,
    {
        async fn keys(&self, count: Option<usize>) -> Result<Vec<I>, async_graphql::Error> {
            let indices = self.indices().await?;
            let it = indices.iter().cloned();
            Ok(if let Some(count) = count {
                it.take(count).collect()
            } else {
                it.collect()
            })
        }

        #[graphql(derived(name = "count"))]
        async fn count_(&self) -> Result<u32, async_graphql::Error> {
            let count = self.iterative_count().await?;
            u32::try_from(count).map_err(|_| async_graphql::Error::new("count exceeds u32"))
        }

        async fn entry(&self, key: I) -> Result<Entry<I, Option<V>>, async_graphql::Error> {
            Ok(Entry {
                value: self.get(&key).await?,
                key,
            })
        }

        async fn entries(
            &self,
            input: Option<MapInput<I>>,
        ) -> Result<Vec<Entry<I, Option<V>>>, async_graphql::Error> {
            let keys = input
                .and_then(|input| input.filters)
                .and_then(|filters| filters.keys);
            let keys = if let Some(keys) = keys {
                keys

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Use `keys(count: n)` / `entries(input: { filters: { keys: [...] } })` for paginated or targeted access instead of `count`.
  2. Prune or archive old map entries so the collection stays representable.
  3. Expose an explicit u64 counter in the application state rather than the derived `count`.
  4. Upstream, widen the field type if >2^32 entries is genuinely needed.

Example fix

# before
query { app { state { balances { count } } } }  # fails past u32::MAX keys

# after
query { app { state { balances { entry(key: "<account>") { value } } } } }  # targeted lookup; track total separately
Defensive patterns

Strategy: fallback

Validate before calling

# targeted entry lookup avoids the count entirely
query { app { state { balances { entry(key: "<account>") { value } } } } }

Try / catch

try { return await gql.query('query { app { state { balances { count } } } }'); } catch (e) { if (/count exceeds u32/i.test(e.message)) { return tallyViaPagedKeys('balances'); } throw e; }

Prevention

When it happens

Trigger: Querying `count` on a MapView that has accumulated more than 4,294,967,295 keys (e.g. per-account records in a very long-lived application).

Common situations: Unbounded per-operation map growth without pruning; synthetic tests filling a map past the boundary.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/0d98bfccbf1f4faf. Report an issue: GitHub.