V3: nested list input coercion rejects valid singleton items

Author: duckkiCreated Sep 24, 2026Updated Sep 24, 2026

Component

GraphQL Engine (c/v3-engine), specifically GraphQL input value normalization in lang-graphql.

What is the current behaviour?

V3 rejects valid nested-list input when an outer list contains singleton values that should be coerced into inner lists.

For example, given an echo-style field:

graphql
type Query {
  echo(value: [[Int]]): [[Int]]!
}

this valid query is rejected during input normalization:

graphql
query {
  echo(value: [1, 2, 3])
}

The underlying normalization error is:

IncorrectFormat { expected_type: "LIST", actual_type: "LIST" }

The same behavior is encoded in test_input_coercion_for_list_example_6, which currently expects [1, 2, 3] to fail for [[Int]].

Observed on master at 94915fe51d6d21bd7f6d4452dc16221bef8cfefd.

What is the expected behaviour?

List coercion should be applied recursively. Each scalar item should be coerced into a singleton inner list, so the resolver should receive [[1], [2], [3]].

For an echo resolver, the response would be:

json
{
  "data": {
    "echo": [[1], [2], [3]]
  }
}

This is the behavior required by the GraphQL specification's List Input Coercion table for input type [[Int]] and input value [1, 2, 3].

How to reproduce the issue?

  1. In v3/crates/graphql/lang-graphql, normalize the parsed constant value [1, 2, 3] at location type [[Int]].
  2. Observe that normalization returns Error::IncorrectFormat.
  3. Compare with the specification result [[1], [2], [3]].

The existing test_input_coercion_for_list_example_6 is also a focused reproduction: it passes because it currently asserts the incorrect rejection.

Screenshots or Screencast

Not applicable.

Please provide any traces or logs that could help here.

The relevant implementation is validation/input/normalize.rs.

While iterating an outer list, it rejects a non-list item whenever the wrapped type is itself a list:

rust
if wrapped_type.is_list() && !v.is_list() {
    Err(Error::IncorrectFormat {
        expected_type: "LIST",
        actual_type: value.kind(),
    })
}

That prevents the recursive singleton coercion required for nested list types.

Any possible solutions/workarounds you're aware of?

Always recursively normalize each list item against wrapped_type. The existing non-list branch of the recursive list normalizer can then wrap a scalar as a singleton list at each nested list level. The explicit wrapped_type.is_list() && !v.is_list() rejection should not be necessary.

Applications can manually send [[1], [2], [3]], but requiring clients to pre-wrap every item is not GraphQL-spec-compliant.

Keywords

GraphQL v3, nested list, list input coercion, singleton coercion, [[Int]], lang-graphql, input normalization