spring-projects / spring-graphql

Spring Integration for GraphQL
https://spring.io/projects/spring-graphql
Apache License 2.0
1.5k stars 297 forks source link

Cannot parse KeysetScrollPosition in native mode #926

Closed juliuskrah closed 3 months ago

juliuskrah commented 4 months ago

Reproducer: https://github.com/juliuskrah/graphql-demo Version: \<1.2.5>

Jackson fails to parse KeysetScrollPosition in native mode.

Steps to reproduce

  1. Schema

    type Query {
     products(first: Int, after: String, last: Int, before: String): ProductConnection
    }
    
    type Mutation {
     addProduct(input: ProductInput!): Product
    }
    
    interface Node {
      id: ID!
    }
    
    type Product implements Node {
     id: ID!
     title: String!
     createdAt: DateTime!
     updatedAt: DateTime!
     description: String
     mediaUrl: [URL]
    }
    
    input ProductInput {
     title: String!
     description: String
     mediaUrl: [URL]
    }
    
    schema {
     query: Query
     mutation: Mutation
    }
  2. Controller

    @Controller
    class ProductController {
    // ...
      @QueryMapping
      suspend fun products(subrange: ScrollSubrange): Window<Product> {
        val scroll = subrange.position().orElse(ScrollPosition.keyset())
        val count = subrange.count().orElse(20)
        return this.nodeService.products(count, scroll).awaitSingle()
      }
    
      @MutationMapping
      suspend fun addProduct(@Argument input: ProductInput): Product {
        return nodeService.product(product = input).awaitSingle()
      }
    }

Build the reproducer ☝🏾 as a native executable and use the API to add some products.

When querying with a cursor e.g.

query products($first: Int, $after: String, $last: Int, $before: String) {
    products(first: $first, after: $after, last: $last, before: $before) {
        edges {
            node {
                id
                title
                createdAt
                updatedAt
                description
                mediaUrl
            }
            cursor
        }
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
    }
}

This fails with an exception:

java.lang.IllegalArgumentException: Failed to parse cursor: K_["java.util.Collections$UnmodifiableMap",{"id":"65f89cfff66d093e95ec7736","updatedAt":["java.time.Instant",1710791935.826000000]}]
    at org.springframework.graphql.data.query.ScrollPositionCursorStrategy.fromCursor(ScrollPositionCursorStrategy.java:91) ~[na:na]
    at org.springframework.graphql.data.query.ScrollPositionCursorStrategy.fromCursor(ScrollPositionCursorStrategy.java:34) ~[na:na]
    at org.springframework.graphql.data.pagination.EncodingCursorStrategy.fromCursor(EncodingCursorStrategy.java:76) ~[graphql-demo:1.2.4]
    at org.springframework.graphql.data.method.annotation.support.SubrangeMethodArgumentResolver.resolveArgument(SubrangeMethodArgumentResolver.java:58) ~[graphql-demo:1.2.4]
    at org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:81) ~[graphql-demo:1.2.4]
    at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.getMethodArgumentValues(DataFetcherHandlerMethod.java:177) ~[na:na]
    at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.invoke(DataFetcherHandlerMethod.java:119) ~[na:na]
    at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.invoke(DataFetcherHandlerMethod.java:107) ~[na:na]
    at org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer$SchemaMappingDataFetcher.get(AnnotatedControllerConfigurer.java:680) ~[na:na]
    at org.springframework.graphql.data.pagination.ConnectionFieldTypeVisitor$ConnectionDataFetcher.get(ConnectionFieldTypeVisitor.java:191) ~[na:na]
    at org.springframework.graphql.execution.ContextDataFetcherDecorator.lambda$get$0(ContextDataFetcherDecorator.java:87) ~[na:na]
    at io.micrometer.context.ContextSnapshot.lambda$wrap$1(ContextSnapshot.java:106) ~[graphql-demo:1.0.6]
    at org.springframework.graphql.execution.ContextDataFetcherDecorator.get(ContextDataFetcherDecorator.java:87) ~[na:na]
    at graphql.execution.ExecutionStrategy.invokeDataFetcher(ExecutionStrategy.java:311) ~[graphql-demo:na]
    at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:287) ~[graphql-demo:na]
    at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:213) ~[graphql-demo:na]
    at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:55) ~[na:na]
    at graphql.execution.Execution.executeOperation(Execution.java:161) ~[na:na]
    at graphql.execution.Execution.execute(Execution.java:103) ~[na:na]
    at graphql.GraphQL.execute(GraphQL.java:568) ~[graphql-demo:na]
    at graphql.GraphQL.lambda$parseValidateAndExecute$13(GraphQL.java:487) ~[graphql-demo:na]
    at java.base@21/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187) ~[graphql-demo:na]
    at java.base@21/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2341) ~[graphql-demo:na]
    at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:482) ~[graphql-demo:na]
    at graphql.GraphQL.lambda$executeAsync$9(GraphQL.java:440) ~[graphql-demo:na]
    at java.base@21/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187) ~[graphql-demo:na]
    at java.base@21/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2341) ~[graphql-demo:na]
    at graphql.GraphQL.executeAsync(GraphQL.java:428) ~[graphql-demo:na]
    at org.springframework.graphql.execution.DefaultExecutionGraphQlService.lambda$execute$2(DefaultExecutionGraphQlService.java:84) ~[na:na]
    at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:47) ~[na:na]
    at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[na:na]
    at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[na:na]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[na:na]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299) ~[na:na]
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337) ~[na:na]
    at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2097) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145) ~[na:na]
    at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[na:na]
    at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[na:na]
    at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[na:na]
    at reactor.netty.channel.FluxReceive.terminateReceiver(FluxReceive.java:483) ~[graphql-demo:1.1.14]
    at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:275) ~[graphql-demo:1.1.14]
    at reactor.netty.channel.FluxReceive.request(FluxReceive.java:133) ~[graphql-demo:1.1.14]
    at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[na:na]
    at reactor.core.publisher.FluxPeek$PeekSubscriber.request(FluxPeek.java:138) ~[na:na]
    at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[na:na]
    at reactor.core.publisher.Operators$BaseFluxToMonoOperator.request(Operators.java:2067) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.request(FluxFilterFuseable.java:411) ~[na:na]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:360) ~[na:na]
    at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.request(FluxContextWrite.java:136) ~[na:na]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2367) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onSubscribe(FluxOnErrorResume.java:74) ~[na:na]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onSubscribe(FluxContextWrite.java:101) ~[na:na]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265) ~[na:na]
    at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onSubscribe(FluxFilterFuseable.java:305) ~[na:na]
    at reactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2051) ~[graphql-demo:3.6.1]
    at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[na:na]
    at reactor.core.publisher.FluxPeek$PeekSubscriber.onSubscribe(FluxPeek.java:171) ~[na:na]
    at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[na:na]
    at reactor.netty.channel.FluxReceive.startReceiver(FluxReceive.java:172) ~[graphql-demo:1.1.14]
    at reactor.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:150) ~[graphql-demo:1.1.14]
    at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173) ~[graphql-demo:4.1.104.Final]
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166) ~[graphql-demo:4.1.104.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[graphql-demo:4.1.104.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) ~[na:na]
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[na:na]
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[na:na]
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[na:na]
    at java.base@21/java.lang.Thread.runWith(Thread.java:1596) ~[graphql-demo:na]
    at java.base@21/java.lang.Thread.run(Thread.java:1583) ~[graphql-demo:na]
    at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:833) ~[graphql-demo:na]
    at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211) ~[na:na]
Caused by: org.springframework.core.codec.DecodingException: JSON decoding error: Could not resolve type id 'java.util.Collections$UnmodifiableMap' as a subtype of `java.util.Map<java.lang.String,java.lang.Object>`: no such class found
    at org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:275) ~[graphql-demo:6.1.2]
    at org.springframework.http.codec.json.AbstractJackson2Decoder.decode(AbstractJackson2Decoder.java:211) ~[graphql-demo:6.1.2]
    at org.springframework.graphql.data.query.JsonKeysetCursorStrategy.fromCursor(JsonKeysetCursorStrategy.java:130) ~[na:na]
    at org.springframework.graphql.data.query.JsonKeysetCursorStrategy.fromCursor(JsonKeysetCursorStrategy.java:55) ~[na:na]
    at org.springframework.graphql.data.query.ScrollPositionCursorStrategy.fromCursor(ScrollPositionCursorStrategy.java:86) ~[na:na]
    ... 76 common frames omitted
Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'java.util.Collections$UnmodifiableMap' as a subtype of `java.util.Map<java.lang.String,java.lang.Object>`: no such class found
 at [Source: (org.springframework.core.io.buffer.DataBufferInputStream); line: 1, column: 42]
    at com.fasterxml.jackson.databind.DeserializationContext.invalidTypeIdException(DeserializationContext.java:2084) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownTypeId(DeserializationContext.java:1575) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:76) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver.typeFromId(ClassNameIdResolver.java:66) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:159) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:97) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:61) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserializeWithType(MapDeserializer.java:492) ~[na:na]
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74) ~[na:na]
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2105) ~[graphql-demo:2.15.3]
    at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1481) ~[graphql-demo:2.15.3]
    at org.springframework.http.codec.json.AbstractJackson2Decoder.decode(AbstractJackson2Decoder.java:206) ~[graphql-demo:6.1.2]
    ... 79 common frames omitted

Skip the mongock migration when running the reproducer export MONGOCK_ENABLED=false

bclozel commented 4 months ago

I can't reproduce the problem on a minimal sample of mine. Your sample is unfortunately far from minimal and fails for me with

io.mongock.api.exception.MongockException: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
    at io.mongock.runner.core.executor.MongockRunnerImpl.execute(MongockRunnerImpl.java:71) ~[na:na]

Can you share a minimal reproducer for this? Ideally, not involving any database nor third party container but the minimal amount of code that focuses on this particular issue. Thanks!

juliuskrah commented 4 months ago

Dear @bclozel thank you for taking the time to look at this issue.

You can disable the migration with export MONGOCK_ENABLED=false.

bclozel commented 3 months ago

Sorry but I still can't reproduce the problem.

After:

I'm getting the following:

{
 products(first: 10) {
        edges {
            node {
                id
                title
                createdAt
                updatedAt
                description
                mediaUrl
            }
            cursor
        }
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
    }
}
{
  "data": {
    "products": {
      "edges": [
        {
          "node": {
            "id": "Z2lkOi8vZGVtby9Qcm9kdWN0LzY2MDNkYTNlNmY5NzNiMWNhZmJmN2NjNg==",
            "title": "Spring",
            "createdAt": "2024-03-27T08:35:10.974Z",
            "updatedAt": "2024-03-27T08:35:10.974Z",
            "description": "spring desc",
            "mediaUrl": [
              "http://example.org"
            ]
          },
          "cursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGEzZTZmOTczYjFjYWZiZjdjYzYiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTAuOTc0MDAwMDAwXX1d"
        },
        {
          "node": {
            "id": "Z2lkOi8vZGVtby9Qcm9kdWN0LzY2MDNkYTQ1NmY5NzNiMWNhZmJmN2NjNw==",
            "title": "Spring",
            "createdAt": "2024-03-27T08:35:17.963Z",
            "updatedAt": "2024-03-27T08:35:17.963Z",
            "description": "spring desc",
            "mediaUrl": [
              "http://example.org"
            ]
          },
          "cursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGE0NTZmOTczYjFjYWZiZjdjYzciLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTcuOTYzMDAwMDAwXX1d"
        },
        {
          "node": {
            "id": "Z2lkOi8vZGVtby9Qcm9kdWN0LzY2MDNkYTQ2NmY5NzNiMWNhZmJmN2NjOA==",
            "title": "Spring",
            "createdAt": "2024-03-27T08:35:18.569Z",
            "updatedAt": "2024-03-27T08:35:18.569Z",
            "description": "spring desc",
            "mediaUrl": [
              "http://example.org"
            ]
          },
          "cursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGE0NjZmOTczYjFjYWZiZjdjYzgiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTguNTY5MDAwMDAwXX1d"
        },
        {
          "node": {
            "id": "Z2lkOi8vZGVtby9Qcm9kdWN0LzY2MDNkYTQ3NmY5NzNiMWNhZmJmN2NjOQ==",
            "title": "Spring",
            "createdAt": "2024-03-27T08:35:19.155Z",
            "updatedAt": "2024-03-27T08:35:19.155Z",
            "description": "spring desc",
            "mediaUrl": [
              "http://example.org"
            ]
          },
          "cursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGE0NzZmOTczYjFjYWZiZjdjYzkiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTkuMTU1MDAwMDAwXX1d"
        },
        {
          "node": {
            "id": "Z2lkOi8vZGVtby9Qcm9kdWN0LzY2MDNkYTQ3NmY5NzNiMWNhZmJmN2NjYQ==",
            "title": "Spring",
            "createdAt": "2024-03-27T08:35:19.378Z",
            "updatedAt": "2024-03-27T08:35:19.378Z",
            "description": "spring desc",
            "mediaUrl": [
              "http://example.org"
            ]
          },
          "cursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGE0NzZmOTczYjFjYWZiZjdjY2EiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTkuMzc4MDAwMDAwXX1d"
        },
        {
          "node": {
            "id": "Z2lkOi8vZGVtby9Qcm9kdWN0LzY2MDNkYTQ3NmY5NzNiMWNhZmJmN2NjYg==",
            "title": "Spring",
            "createdAt": "2024-03-27T08:35:19.591Z",
            "updatedAt": "2024-03-27T08:35:19.591Z",
            "description": "spring desc",
            "mediaUrl": [
              "http://example.org"
            ]
          },
          "cursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGE0NzZmOTczYjFjYWZiZjdjY2IiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTkuNTkxMDAwMDAwXX1d"
        }
      ],
      "pageInfo": {
        "hasNextPage": false,
        "hasPreviousPage": false,
        "startCursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGEzZTZmOTczYjFjYWZiZjdjYzYiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTAuOTc0MDAwMDAwXX1d",
        "endCursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjAzZGE0NzZmOTczYjFjYWZiZjdjY2IiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTE1Mjg1MTkuNTkxMDAwMDAwXX1d"
      }
    }
  }
}
2024-03-27T09:35:29.648+01:00  INFO 14404 --- [ctor-http-nio-3] com.example.graph.NodeController         : Using forward=true and count=10 with position=KeysetScrollPosition [FORWARD, {}]

Unfortunately, I can't justify spending more time on this. If you can consistently reproduce the problem, please provide a minimal sample this time.

juliuskrah commented 2 months ago

Apologies @bclozel, it took me so long to return to this topic, I was on vacation. I created a minimal reproducer on sfg-926 branch.

{
 products(first: 10, after: "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsiaWQiOiI2NjJmNTVjMzNhNWYwOTIwZTMxNTUxYjkiLCJ1cGRhdGVkQXQiOlsiamF2YS50aW1lLkluc3RhbnQiLDE3MTQzNzgxNzkuNTExMDAwMDAwXX1d") {
        edges {
            node {
                id
                title
                createdAt
                updatedAt
                description
                mediaUrl
            }
            cursor
        }
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
    }
}

Take note of the after argument, it is key to reproducing this bug.

juliuskrah commented 1 week ago

@bclozel I created a simplified reproducer, can you reopen this issue?