ChilliCream / ChilliCream/graphql-platform

Subgraph transport exception for fields below an entity cause entire entity to be nulled

Open
#6,752 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

🌶️ hot chocolate Area: Fusion
Dominant language
C#
Stars
5.8k
Forks
810
Avg merge
15h 39m
Merged PRs (30d)
98

Description

Is there an existing issue for this?
  • I have searched the existing issues
Product

Hot Chocolate

Describe the bug

If two or more subgraphs expose an entity via a top-level resolver like productById and one of the subgraphs fails to provide their part of the entity, the entire entity resolver result is nulled, even if the top-most fields below Product requested from the faulty subgraph are nullable.

For a query like this:

query Product {
  productById(id: "UHJvZHVjdAppMQ==") {
    # Subgraph: Product
    name
    # Subgraph: Reviews
    reviews { # nullable
      body
    }
  }
}

My expectation would be a response like this, if the reviews Subgraph can not be reached:

{
  "errors": [
    // Errors for each top-most field that couldn't be resolved from the subgraph
    //(in this case just `reviews`)
  ],
  "data": {
    "productById":{
      "name": "iPhone",
      "reviews": null
    }
  }
}

The actual result you'd get is:

{
  "errors": [
    {
      "message": "Internal Execution Error"
    }
  ],
  "data": {
    "productById": null
  }
}

This is pretty bad, since a single field being requested from a faulty subgraph could take down an entire page!
The behavior for top-level query fields being resolved from different subgraphs is already correct.

Steps to reproduce

I've created an integration test showcasing this:

[Fact]
public async Task Test()
{
    // arrange
    using var demoProject = await DemoProject.CreateAsync();

    var fusionGraph = await new FusionGraphComposer(logFactory: _logFactory).ComposeAsync(
        new[]
        {
            demoProject.Products.ToConfiguration(ProductsExtensionSdl),
            demoProject.Reviews.ToConfiguration(ReviewsExtensionSdl),
        },
        new FusionFeatureCollection(FusionFeatures.NodeField));

    var executor = await new ServiceCollection()
        .AddSingleton(demoProject.HttpClientFactory)
        .AddSingleton(demoProject.WebSocketConnectionFactory)
        .AddSingleton<IConfigurationRewriter, CustomRewriter>()
        .AddFusionGatewayServer()
        .ConfigureFromDocument(SchemaFormatter.FormatAsDocument(fusionGraph))
        .BuildRequestExecutorAsync();

    var request = Parse(
        """
        query Product {
          productById(id: "UHJvZHVjdAppMQ==") {
            # Subgraph: Product
            name
            # Subgraph: Reviews
            reviews {
              body
            }
          }
        }
        """);

    // act
    await using var result = await executor.ExecuteAsync(
        QueryRequestBuilder
            .New()
            .SetQuery(request)
            .Create());

    // assert
    var snapshot = new Snapshot();
    CollectSnapshotData(snapshot, request, result, fusionGraph);
    await snapshot.MatchAsync();

    Assert.Null(result.ExpectQueryResult().Errors);
}

private class CustomRewriter : ConfigurationRewriter
{
    protected override ValueTask<Metadata.HttpClientConfiguration> RewriteAsync(
        Metadata.HttpClientConfiguration configuration,
        CancellationToken cancellationToken)
    {
        if (configuration.ClientName == "Reviews")
        {
            // This simulates the Reviews subgraph not being reachable
            return base.RewriteAsync(
                configuration with { EndpointUri = new Uri("http://client") },
                cancellationToken);
        }

        return base.RewriteAsync(configuration, cancellationToken);
    }
}
Relevant log output

No response

Additional Context?

No response

Version

14.0.0-p.15

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the integration test in the issue, including FusionGraphComposer, AddFusionGatewayServer, and CustomRewriter, and run it against the failing Reviews-subgraph scenario. Inspect the execution path for entity fields below productById. Done means the snapshot shows productById and name preserved, reviews set to null, and errors limited to the failed top-level field.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, graphql
Domain
api, backend, distributed-systems, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.