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

count exceeds u32

Error message

count exceeds u32

What it means

The GraphQL schema generated for `CollectionView` exposes a derived `count` field typed u32. The real entry count is a `usize` computed by iterating the view's index (`iterative_count`); if it exceeds `u32::MAX` (4,294,967,295) the conversion fails and the resolver returns 'count exceeds u32'. It is effectively an overflow guard for astronomically large collections, not an everyday error.

Source

Thrown at linera-views/src/views/collection_view.rs:1830

    #[async_graphql::Object(cache_control(no_cache), name_type)]
    impl<K, V> CollectionView<V::Context, K, V>
    where
        K: async_graphql::InputType
            + async_graphql::OutputType
            + serde::ser::Serialize
            + serde::de::DeserializeOwned
            + std::fmt::Debug,
        V: View + async_graphql::OutputType,
    {
        async fn keys(&self) -> Result<Vec<K>, async_graphql::Error> {
            Ok(self.indices().await?)
        }

        #[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: K,
        ) -> Result<Entry<K, ReadGuardedView<'_, V>>, async_graphql::Error> {
            let value = self
                .try_load_entry(&key)
                .await?
                .ok_or_else(|| missing_key_error(&key))?;
            Ok(Entry { value, key })
        }

        async fn entries(
            &self,
            input: Option<MapInput<K>>,
        ) -> Result<Vec<Entry<K, ReadGuardedView<'_, V>>>, async_graphql::Error> {
            let keys = if let Some(keys) = input

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Avoid `count` for huge views: page through `keys`/`entries` (with the `count`/filter arguments) and aggregate client-side if an exact total is not required.
  2. Redesign the application state to bound growth — prune, bucket, or archive old entries into separate views or blobs.
  3. Maintain an explicit counter (e.g. a RegisterView<u64>) updated on insert/remove instead of relying on the view's `count`.
  4. If you truly need more than 2^32 entries, request an upstream schema change to widen the field; the current u32 cannot represent it.

Example fix

# before
query { app { state { users { count } } } }  # errors once > u32::MAX entries

# after
query { app { state { users { keys(count: 1000) } } } }  # page and tally client-side; or expose apps { userCount }
Defensive patterns

Strategy: fallback

Validate before calling

# GraphQL: probe size cheaply before trusting `count`
query { app { state { users { keys(count: 1) } } } }  # returns at least the keys page without u32 risk

Try / catch

try { return await gql.query('query { app { state { users { count } } } }'); } catch (e) { if (/count exceeds u32/i.test(e.message)) { const keys = await gql.query('query { app { state { users { keys } } } }'); return countKeysByPaging(keys); } throw e; }

Prevention

When it happens

Trigger: Querying `count` on a CollectionView (e.g. an application's `users: CollectionView<_, Owner, RegisterView<_, ...>>`) holding more than 4,294,967,295 entries.

Common situations: Long-running, high-throughput applications writing one entry per operation without pruning; stress tests that inflate views to probe limits.

Related errors


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