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

count exceeds u32

Error message

count exceeds u32

What it means

The GraphQL schema generated for `ReentrantCustomCollectionView` exposes a derived `count` field typed u32. The entry count is a `usize` from `iterative_count`; when it exceeds `u32::MAX` the narrowing conversion fails with 'count exceeds u32'. Only collections beyond ~4.29 billion entries can trigger it — it is a schema-limit guard.

Source

Thrown at linera-views/src/views/reentrant_collection_view.rs:2353

    #[async_graphql::Object(cache_control(no_cache), name_type)]
    impl<K, V> ReentrantCollectionView<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. Page via `keys`/`entries` and aggregate client-side instead of calling `count` on giant views.
  2. Bound growth by pruning or sharding entries across multiple views.
  3. Maintain an explicit u64 counter in application state.
  4. Request an upstream change widening the GraphQL field if >2^32 entries is a real requirement.

Example fix

# before
query { app { state { records { count } } } }  # 'count exceeds u32'

# after
query { app { state { records { keys(count: 1000) } } } }  # paginate; expose recordsTotal as u64 in the app
Defensive patterns

Strategy: fallback

Validate before calling

# page a single key first
query { app { state { records { keys(count: 1) } } } }

Try / catch

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

Prevention

When it happens

Trigger: Querying `count` on a ReentrantCustomCollectionView (views containing other views as values) holding more than 4,294,967,295 entries.

Common situations: Long-lived applications storing one nested view per operation with no pruning; adversarial/stress tests deliberately exceeding the u32 boundary.

Related errors


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