STET

agent_patch

runs/2026-02-28__13-38-10__gpt-5-4/flux-pr-1297/flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/agent-logs/agent.patch

27148 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/configuration.go b/app/v2/pkg/engine/datasource/graphql_datasource/configuration.go
index c5a7f3f..824d5a2 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/configuration.go
+++ b/app/v2/pkg/engine/datasource/graphql_datasource/configuration.go
@@ -128,8 +128,9 @@ type FetchConfiguration struct {
 }
 
 type FederationConfiguration struct {
-	Enabled    bool
-	ServiceSDL string
+	Enabled                              bool
+	ServiceSDL                           string
+	ValidateNullableRequiresDependencies bool
 }
 
 type SchemaConfiguration struct {
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go b/app/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go
index 3acdd07..b5c8ac1 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go
+++ b/app/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go
@@ -844,7 +844,12 @@ func (p *Planner[T]) addRepresentationsVariable() {
 func (p *Planner[T]) buildRepresentationsVariable() resolve.Variable {
 	objects := make([]*resolve.Object, 0, len(p.dataSourcePlannerConfig.RequiredFields))
 	for _, cfg := range p.dataSourcePlannerConfig.RequiredFields {
-		node, err := buildRepresentationVariableNode(p.visitor.Definition, cfg, p.dataSourceConfig.FederationConfiguration())
+		node, err := buildRepresentationVariableNode(
+			p.visitor.Definition,
+			cfg,
+			p.dataSourceConfig.FederationConfiguration(),
+			p.config.FederationConfiguration() != nil && p.config.FederationConfiguration().ValidateNullableRequiresDependencies,
+		)
 		if err != nil {
 			p.stopWithError(errors.WithStack(fmt.Errorf("buildRepresentationsVariable: failed to build representation variable node: %w", err)))
 			return nil
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable.go b/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable.go
index 18cb3ad..441a3c5 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable.go
+++ b/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable.go
@@ -18,7 +18,7 @@ type objectFields struct {
 
 // TODO: add support for remapping path
 
-func buildRepresentationVariableNode(definition *ast.Document, cfg plan.FederationFieldConfiguration, federationCfg plan.FederationMetaData) (*resolve.Object, error) {
+func buildRepresentationVariableNode(definition *ast.Document, cfg plan.FederationFieldConfiguration, federationCfg plan.FederationMetaData, validateRequiresDependencies bool) (*resolve.Object, error) {
 	key, report := plan.RequiredFieldsFragment(cfg.TypeName, cfg.SelectionSet, false)
 	if report.HasErrors() {
 		return nil, report
@@ -43,13 +43,14 @@ func buildRepresentationVariableNode(definition *ast.Document, cfg plan.Federati
 	}
 
 	visitor := &representationVariableVisitor{
-		typeName:                cfg.TypeName,
-		interfaceObjectTypeName: interfaceObjectTypeName,
-		entityInterfaceTypeName: entityInterfaceTypeName,
-		addOnType:               true,
-		addTypeName:             true,
-		remapPaths:              cfg.RemappedPaths,
-		Walker:                  walker,
+		typeName:                     cfg.TypeName,
+		interfaceObjectTypeName:      interfaceObjectTypeName,
+		entityInterfaceTypeName:      entityInterfaceTypeName,
+		addOnType:                    true,
+		addTypeName:                  true,
+		remapPaths:                   cfg.RemappedPaths,
+		validateRequiresDependencies: validateRequiresDependencies && cfg.FieldName != "",
+		Walker:                       walker,
 	}
 	walker.RegisterEnterDocumentVisitor(visitor)
 	walker.RegisterFieldVisitor(visitor)
@@ -159,6 +160,8 @@ type representationVariableVisitor struct {
 	addOnType   bool
 	addTypeName bool
 	remapPaths  map[string]string
+
+	validateRequiresDependencies bool
 }
 
 func (v *representationVariableVisitor) EnterDocument(key, definition *ast.Document) {
@@ -219,9 +222,10 @@ func (v *representationVariableVisitor) EnterField(ref int) {
 	}
 
 	currentField := &resolve.Field{
-		Name:        fieldName,
-		Value:       v.resolveFieldValue(ref, fieldDefinitionType, true, []string{fieldPath}),
-		OnTypeNames: v.resolveOnTypeNames(ref),
+		Name:             fieldName,
+		Value:            v.resolveFieldValue(ref, fieldDefinitionType, true, []string{fieldPath}),
+		OnTypeNames:      v.resolveOnTypeNames(ref),
+		ValidatePresence: v.validateRequiresDependencies && !v.definition.TypeIsNonNull(fieldDefinitionType),
 	}
 
 	if v.addOnType && v.currentFields[len(v.currentFields)-1].isRoot {
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable_test.go b/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable_test.go
index e26d1a3..c1703f6 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable_test.go
+++ b/app/v2/pkg/engine/datasource/graphql_datasource/representation_variable_test.go
@@ -11,14 +11,17 @@ import (
 )
 
 func TestBuildRepresentationVariableNode(t *testing.T) {
-	runTest := func(t *testing.T, definitionStr, keyStr string, federationMeta plan.FederationMetaData, expectedNode resolve.Node) {
+	runTest := func(t *testing.T, definitionStr, keyStr string, federationMeta plan.FederationMetaData, validateRequiresDependencies bool, expectedNode resolve.Node) {
 		definition, _ := astparser.ParseGraphqlDocumentString(definitionStr)
 		cfg := plan.FederationFieldConfiguration{
 			TypeName:     "User",
 			SelectionSet: keyStr,
 		}
+		if validateRequiresDependencies {
+			cfg.FieldName = "name"
+		}
 
-		node, err := buildRepresentationVariableNode(&definition, cfg, federationMeta)
+		node, err := buildRepresentationVariableNode(&definition, cfg, federationMeta, validateRequiresDependencies)
 		require.NoError(t, err)
 
 		require.Equal(t, expectedNode, node)
@@ -35,6 +38,7 @@ func TestBuildRepresentationVariableNode(t *testing.T) {
 		`,
 			`id name`,
 			plan.FederationMetaData{},
+			false,
 			&resolve.Object{
 				Nullable: true,
 				Fields: []*resolve.Field{
@@ -81,6 +85,7 @@ func TestBuildRepresentationVariableNode(t *testing.T) {
 					},
 				},
 			},
+			false,
 			&resolve.Object{
 				Nullable: true,
 				Fields: []*resolve.Field{
@@ -131,9 +136,10 @@ func TestBuildRepresentationVariableNode(t *testing.T) {
 				zip: Float!
 			}
 				
-		`,
+			`,
 			`id name account { accoundID address(home: true) { zip } }`,
 			plan.FederationMetaData{},
+			false,
 			&resolve.Object{
 				Nullable: true,
 				Fields: []*resolve.Field{
@@ -217,9 +223,10 @@ func TestBuildRepresentationVariableNode(t *testing.T) {
 			}
 
 		    union ab = A | B
-		`,
+			`,
 			`u { ... on A { a } } i { ... on B { title } }`,
 			plan.FederationMetaData{},
+			false,
 			&resolve.Object{
 				Nullable: true,
 				Fields: []*resolve.Field{
@@ -265,6 +272,62 @@ func TestBuildRepresentationVariableNode(t *testing.T) {
 				},
 			})
 	})
+
+	t.Run("nullable requires fields are marked for validation", func(t *testing.T) {
+		runTest(t, `
+			scalar String
+
+			type User {
+				id: String!
+				details: Details
+			}
+
+			type Details {
+				middlename: String
+			}
+		`,
+			`id details { middlename }`,
+			plan.FederationMetaData{},
+			true,
+			&resolve.Object{
+				Nullable: true,
+				Fields: []*resolve.Field{
+					{
+						Name: []byte("__typename"),
+						Value: &resolve.String{
+							Path: []string{"__typename"},
+						},
+						OnTypeNames: [][]byte{[]byte("User")},
+					},
+					{
+						Name: []byte("id"),
+						Value: &resolve.String{
+							Path: []string{"id"},
+						},
+						OnTypeNames: [][]byte{[]byte("User")},
+					},
+					{
+						Name:             []byte("details"),
+						ValidatePresence: true,
+						Value: &resolve.Object{
+							Path:     []string{"details"},
+							Nullable: true,
+							Fields: []*resolve.Field{
+								{
+									Name:             []byte("middlename"),
+									ValidatePresence: true,
+									Value: &resolve.String{
+										Path:     []string{"middlename"},
+										Nullable: true,
+									},
+								},
+							},
+						},
+						OnTypeNames: [][]byte{[]byte("User")},
+					},
+				},
+			})
+	})
 }
 
 func TestMergeRepresentationVariableNodes(t *testing.T) {
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/loader.go b/app/v2/pkg/engine/resolve/loader.go
index 1e9f993..21bf134 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/loader.go
+++ b/app/v2/pkg/engine/resolve/loader.go
@@ -27,6 +27,7 @@ import (
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/datasource/httpclient"
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/errorcodes"
+	"github.com/wundergraph/graphql-go-tools/v2/pkg/fastjsonext"
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafebytes"
 )
 
@@ -139,6 +140,8 @@ type result struct {
 	loaderHookContext context.Context
 
 	httpResponseContext *httpclient.ResponseContext
+
+	missingRequiredDependencyErrorPaths [][]fastjsonext.PathElement
 }
 
 func (r *result) init(postProcessing PostProcessingConfiguration, info *FetchInfo) {
@@ -540,6 +543,7 @@ func (l *Loader) mergeResult(fetchItem *FetchItem, res *result, items []*astjson
 		}
 		return nil
 	}
+	l.renderMissingRequiredDependencyErrors(res)
 	if res.fetchSkipped {
 		return nil
 	}
@@ -567,6 +571,7 @@ func (l *Loader) mergeResult(fetchItem *FetchItem, res *result, items []*astjson
 			// If errors field are present in response, but the errors array is empty, we don't consider it as an error
 			// Note: it is not compliant to graphql spec
 			if hasErrors {
+				l.markMissingRequiredDependencies(fetchItem, res, items, errorObjects)
 				// Look for errors in the response and merge them into the errors array
 				err = l.mergeErrors(res, fetchItem, errorsValue, errorObjects)
 				if err != nil {
@@ -669,6 +674,112 @@ func (l *Loader) mergeResult(fetchItem *FetchItem, res *result, items []*astjson
 	return nil
 }
 
+func (l *Loader) renderMissingRequiredDependencyErrors(res *result) {
+	for i := range res.missingRequiredDependencyErrorPaths {
+		fastjsonext.AppendErrorToArray(l.resolvable.astjsonArena, l.resolvable.errors, missingRequiredFederationDependencyMessage, res.missingRequiredDependencyErrorPaths[i])
+	}
+}
+
+func (l *Loader) markMissingRequiredDependencies(fetchItem *FetchItem, res *result, items []*astjson.Value, errorObjects []*astjson.Value) {
+	switch fetchItem.Fetch.(type) {
+	case *EntityFetch, *BatchEntityFetch:
+	default:
+		return
+	}
+
+	trueValue := astjson.MustParse(`true`)
+	for _, errorObject := range errorObjects {
+		errorPath := errorObject.Get("path")
+		if errorPath == nil || errorPath.Type() != astjson.TypeArray {
+			continue
+		}
+		pathItems := errorPath.GetArray()
+		if len(pathItems) < 3 {
+			continue
+		}
+		if string(pathItems[0].GetStringBytes()) != "_entities" {
+			continue
+		}
+
+		entityIndex, ok := l.pathItemIndex(pathItems[1])
+		if !ok {
+			continue
+		}
+
+		markerSuffix := make([]string, 0, len(pathItems)-2)
+		for i := 2; i < len(pathItems); i++ {
+			switch pathItems[i].Type() {
+			case astjson.TypeString:
+				markerSuffix = append(markerSuffix, string(pathItems[i].GetStringBytes()))
+			case astjson.TypeNumber:
+				markerSuffix = append(markerSuffix, strconv.Itoa(pathItems[i].GetInt()))
+			}
+		}
+		if len(markerSuffix) == 0 {
+			continue
+		}
+
+		for _, itemIndex := range l.matchItemsForBatchIndex(items, res, entityIndex) {
+			if itemIndex < 0 || itemIndex >= len(items) || items[itemIndex] == nil {
+				continue
+			}
+			astjson.SetValue(items[itemIndex], trueValue, append([]string{requiresDependencyTaintKey}, markerSuffix...)...)
+		}
+	}
+}
+
+func (l *Loader) matchItemsForBatchIndex(items []*astjson.Value, res *result, entityIndex int) []int {
+	if res.batchStats == nil {
+		if entityIndex >= 0 && entityIndex < len(items) {
+			return []int{entityIndex}
+		}
+		if entityIndex == 0 && len(items) == 1 {
+			return []int{0}
+		}
+		return nil
+	}
+
+	matches := make([]int, 0, len(items))
+	for i := range res.batchStats {
+		for _, batchIndex := range res.batchStats[i] {
+			if batchIndex == entityIndex {
+				matches = append(matches, i)
+				break
+			}
+		}
+	}
+	return matches
+}
+
+func (l *Loader) pathItemIndex(value *astjson.Value) (int, bool) {
+	switch value.Type() {
+	case astjson.TypeNumber:
+		return value.GetInt(), true
+	case astjson.TypeString:
+		index, err := strconv.Atoi(string(value.GetStringBytes()))
+		if err != nil {
+			return 0, false
+		}
+		return index, true
+	default:
+		return 0, false
+	}
+}
+
+func (l *Loader) missingDependencyErrorPath(fetchItem *FetchItem, itemIndex, totalItems int) []fastjsonext.PathElement {
+	path := make([]fastjsonext.PathElement, 0, len(fetchItem.ResponsePathElements)+1)
+	for i := range fetchItem.ResponsePathElements {
+		if totalItems > 1 && i == len(fetchItem.ResponsePathElements)-1 {
+			path = append(path, fastjsonext.PathElement{Idx: itemIndex})
+		}
+		path = append(path, fastjsonext.PathElement{Name: fetchItem.ResponsePathElements[i]})
+	}
+	if totalItems > 1 && len(fetchItem.ResponsePathElements) == 0 {
+		path = append(path, fastjsonext.PathElement{Idx: itemIndex})
+	}
+	return path
+}
+
 var (
 	errorsInvalidInputHeader = []byte(`{"errors":[{"message":"Failed to render Fetch Input","path":[`)
 	errorsInvalidInputFooter = []byte(`]}]}`)
@@ -1311,6 +1422,9 @@ func (l *Loader) loadEntityFetch(ctx context.Context, fetchItem *FetchItem, fetc
 	err = fetch.Input.Item.Render(l.ctx, input, buf.item)
 	if err != nil {
 		if fetch.Input.SkipErrItem {
+			if goerrors.Is(err, errMissingRequiredFederationDependency) {
+				res.missingRequiredDependencyErrorPaths = append(res.missingRequiredDependencyErrorPaths, l.missingDependencyErrorPath(fetchItem, 0, len(items)))
+			}
 			// skip fetch on render item error
 			if l.ctx.TracingOptions.Enable {
 				fetch.Trace.LoadSkipped = true
@@ -1430,6 +1544,9 @@ WithNextItem:
 			err = fetch.Input.Items[j].Render(l.ctx, item, buf.itemInput)
 			if err != nil {
 				if fetch.Input.SkipErrItems {
+					if goerrors.Is(err, errMissingRequiredFederationDependency) {
+						res.missingRequiredDependencyErrorPaths = append(res.missingRequiredDependencyErrorPaths, l.missingDependencyErrorPath(fetchItem, i, len(items)))
+					}
 					err = nil // nolint:ineffassign
 					res.batchStats[i] = append(res.batchStats[i], -1)
 					continue
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/node_object.go b/app/v2/pkg/engine/resolve/node_object.go
index 7f5e94a..f835c6a 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/node_object.go
+++ b/app/v2/pkg/engine/resolve/node_object.go
@@ -95,6 +95,7 @@ type Field struct {
 	OnTypeNames       [][]byte
 	ParentOnTypeNames []ParentOnTypeNames
 	Info              *FieldInfo
+	ValidatePresence  bool
 }
 
 type ParentOnTypeNames struct {
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/resolvable.go b/app/v2/pkg/engine/resolve/resolvable.go
index 5219c91..c3de3bd 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/resolvable.go
+++ b/app/v2/pkg/engine/resolve/resolvable.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
+	stdErrors "errors"
 	"fmt"
 	"io"
 	"strconv"
@@ -23,6 +24,13 @@ import (
 
 const invalidPath = "invalid path"
 
+const (
+	missingRequiredFederationDependencyMessage = "Missing required federation dependency."
+	requiresDependencyTaintKey                 = "__wg__fed__requires"
+)
+
+var errMissingRequiredFederationDependency = stdErrors.New("missing required federation dependency")
+
 type Resolvable struct {
 	options ResolvableOptions
 
@@ -58,6 +66,9 @@ type Resolvable struct {
 	enclosingTypeNames []string
 
 	currentFieldInfo *FieldInfo
+
+	variableRoot        *astjson.Value
+	nodeResolutionError error
 }
 
 type ResolvableOptions struct {
@@ -97,6 +108,8 @@ func (r *Resolvable) Reset() {
 	r.authorizationError = nil
 	r.astjsonArena.Reset()
 	r.xxh.Reset()
+	r.variableRoot = nil
+	r.nodeResolutionError = nil
 	for k := range r.authorizationAllow {
 		delete(r.authorizationAllow, k)
 	}
@@ -169,9 +182,14 @@ func (r *Resolvable) ResolveNode(node Node, data *astjson.Value, out io.Writer)
 	r.printErr = nil
 	r.authorizationError = nil
 	r.errors = r.astjsonArena.NewArray()
+	r.variableRoot = data
+	r.nodeResolutionError = nil
 
 	hasErrors := r.walkNode(node, data)
 	if hasErrors {
+		if r.nodeResolutionError != nil {
+			return r.nodeResolutionError
+		}
 		return fmt.Errorf("error resolving node")
 	}
 
@@ -668,9 +686,16 @@ func (r *Resolvable) walkObject(obj *Object, parent *astjson.Value) bool {
 			r.printBytes(quote)
 			r.printBytes(colon)
 		}
+		if !r.print && obj.Fields[i].ValidatePresence && r.hasMissingRequiredFederationDependency(value, obj.Fields[i].Value.NodePath()) {
+			r.nodeResolutionError = errMissingRequiredFederationDependency
+			return true
+		}
 		r.currentFieldInfo = obj.Fields[i].Info
 		err := r.walkNode(obj.Fields[i].Value, value)
 		if err {
+			if r.nodeResolutionError != nil {
+				return true
+			}
 			if obj.Nullable {
 				if len(obj.Path) > 0 {
 					astjson.SetNull(parent, obj.Path...)
@@ -852,6 +877,9 @@ func (r *Resolvable) walkArray(arr *Array, value *astjson.Value) bool {
 		err := r.walkNode(arr.Item, arrayValue)
 		r.popArrayPathElement()
 		if err {
+			if r.nodeResolutionError != nil {
+				return true
+			}
 			if arr.Item.NodeKind() == NodeKindObject && arr.Item.NodeNullable() {
 				value.SetArrayItem(i, astjson.NullValue)
 				continue
@@ -869,6 +897,29 @@ func (r *Resolvable) walkArray(arr *Array, value *astjson.Value) bool {
 	return false
 }
 
+func (r *Resolvable) hasMissingRequiredFederationDependency(value *astjson.Value, path []string) bool {
+	if r.variableRoot == nil || len(path) == 0 {
+		return false
+	}
+	fieldValue := value.Get(path...)
+	if !astjson.ValueIsNull(fieldValue) {
+		return false
+	}
+
+	markerPath := make([]string, 0, len(r.path)+len(path)+1)
+	markerPath = append(markerPath, requiresDependencyTaintKey)
+	for i := range r.path {
+		if r.path[i].Name != "" {
+			markerPath = append(markerPath, r.path[i].Name)
+		} else {
+			markerPath = append(markerPath, strconv.Itoa(r.path[i].Idx))
+		}
+	}
+	markerPath = append(markerPath, path...)
+
+	return astjson.ValueIsNonNull(r.variableRoot.Get(markerPath...))
+}
+
 func (r *Resolvable) walkNull() bool {
 	if r.print {
 		r.printBytes(null)
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/resolve_federation_test.go b/app/v2/pkg/engine/resolve/resolve_federation_test.go
index 2547c6d..5a73665 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/resolve_federation_test.go
+++ b/app/v2/pkg/engine/resolve/resolve_federation_test.go
@@ -9,6 +9,8 @@ import (
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+
+	"github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
 )
 
 type TestingTB interface {
@@ -2148,6 +2150,210 @@ func TestResolveGraphQLResponse_Federation(t *testing.T) {
 		}, Context{ctx: context.Background()}, `{"data":{"user":{"account":{"address":{"fullAddress":"line1 line2 line3-1 city-1 country-1 zip-1"}}}}}`
 	}))
 
+	t.Run("skips tainted nullable requires dependencies", func(t *testing.T) {
+		ctrl := gomock.NewController(t)
+		rCtx, cancel := context.WithCancel(context.Background())
+		defer cancel()
+
+		secondService := NewMockDataSource(ctrl)
+		secondService.EXPECT().
+			Load(gomock.Any(), gomock.Any(), gomock.AssignableToTypeOf(&bytes.Buffer{})).
+			DoAndReturn(func(ctx context.Context, input []byte, w io.Writer) error {
+				require.Equal(t,
+					`{"method":"POST","url":"http://second.service","body":{"query":"query($representations: [_Any!]!){_entities(representations: $representations){__typename ... on User {details {middlename}}}}","variables":{"representations":[{"__typename":"User","id":"1"}]}}}`,
+					string(input),
+				)
+
+				pair := NewBufPair()
+				pair.Data.WriteString(`{"_entities":[{"__typename":"User","details":{"middlename":null}}]}`)
+				pair.Errors.WriteString(`{"message":"boom","path":["_entities",0,"details","middlename"]}`)
+				return writeGraphqlResponse(pair, w, false)
+			})
+
+		node := &GraphQLResponse{
+			Info: &GraphQLResponseInfo{
+				OperationType: ast.OperationTypeQuery,
+			},
+			Fetches: Sequence(
+				Single(&SingleFetch{
+					InputTemplate: InputTemplate{
+						Segments: []TemplateSegment{
+							{
+								Data:        []byte(`{"method":"POST","url":"http://first.service","body":{"query":"{me {__typename id}}"}}`),
+								SegmentType: StaticSegmentType,
+							},
+						},
+					},
+					FetchConfiguration: FetchConfiguration{
+						DataSource: FakeDataSource(`{"data":{"me":{"__typename":"User","id":"1"}}}`),
+						PostProcessing: PostProcessingConfiguration{
+							SelectResponseDataPath: []string{"data"},
+						},
+					},
+				}),
+				SingleWithPath(&EntityFetch{
+					FetchDependencies: FetchDependencies{
+						FetchID:           1,
+						DependsOnFetchIDs: []int{0},
+					},
+					Input: EntityInput{
+						Header: InputTemplate{
+							Segments: []TemplateSegment{{
+								Data:        []byte(`{"method":"POST","url":"http://second.service","body":{"query":"query($representations: [_Any!]!){_entities(representations: $representations){__typename ... on User {details {middlename}}}}","variables":{"representations":[`),
+								SegmentType: StaticSegmentType,
+							}},
+						},
+						Item: InputTemplate{
+							Segments: []TemplateSegment{{
+								SegmentType:  VariableSegmentType,
+								VariableKind: ResolvableObjectVariableKind,
+								Renderer: NewGraphQLVariableResolveRenderer(&Object{
+									Nullable: true,
+									Fields: []*Field{
+										{
+											Name: []byte("__typename"),
+											Value: &String{
+												Path: []string{"__typename"},
+											},
+										},
+										{
+											Name: []byte("id"),
+											Value: &String{
+												Path: []string{"id"},
+											},
+										},
+									},
+								}),
+							}},
+						},
+						Footer: InputTemplate{
+							Segments: []TemplateSegment{{
+								Data:        []byte(`]}}}`),
+								SegmentType: StaticSegmentType,
+							}},
+						},
+						SkipErrItem: true,
+					},
+					DataSource: secondService,
+					PostProcessing: PostProcessingConfiguration{
+						SelectResponseDataPath:   []string{"data", "_entities", "0"},
+						SelectResponseErrorsPath: []string{"errors"},
+					},
+				}, "me", ObjectPath("me")),
+				SingleWithPath(&EntityFetch{
+					FetchDependencies: FetchDependencies{
+						FetchID:           2,
+						DependsOnFetchIDs: []int{1},
+					},
+					Input: EntityInput{
+						Header: InputTemplate{
+							Segments: []TemplateSegment{{
+								Data:        []byte(`{"method":"POST","url":"http://third.service","body":{"query":"query($representations: [_Any!]!){_entities(representations: $representations){__typename ... on User {details {age}}}}","variables":{"representations":[`),
+								SegmentType: StaticSegmentType,
+							}},
+						},
+						Item: InputTemplate{
+							Segments: []TemplateSegment{{
+								SegmentType:  VariableSegmentType,
+								VariableKind: ResolvableObjectVariableKind,
+								Renderer: NewGraphQLVariableResolveRenderer(&Object{
+									Nullable: true,
+									Fields: []*Field{
+										{
+											Name: []byte("__typename"),
+											Value: &String{
+												Path: []string{"__typename"},
+											},
+										},
+										{
+											Name: []byte("id"),
+											Value: &String{
+												Path: []string{"id"},
+											},
+										},
+										{
+											Name:             []byte("details"),
+											ValidatePresence: true,
+											Value: &Object{
+												Path:     []string{"details"},
+												Nullable: true,
+												Fields: []*Field{
+													{
+														Name:             []byte("middlename"),
+														ValidatePresence: true,
+														Value: &String{
+															Path:     []string{"middlename"},
+															Nullable: true,
+														},
+													},
+												},
+											},
+										},
+									},
+								}),
+							}},
+						},
+						Footer: InputTemplate{
+							Segments: []TemplateSegment{{
+								Data:        []byte(`]}}}`),
+								SegmentType: StaticSegmentType,
+							}},
+						},
+						SkipErrItem: true,
+					},
+					DataSource: FakeDataSource(`{"data":{"_entities":[{"__typename":"User","details":{"age":42}}]}}`),
+					PostProcessing: PostProcessingConfiguration{
+						SelectResponseDataPath: []string{"data", "_entities", "0"},
+					},
+				}, "me.details.age", ObjectPath("me")),
+			),
+			Data: &Object{
+				Fields: []*Field{
+					{
+						Name: []byte("me"),
+						Value: &Object{
+							Path:     []string{"me"},
+							Nullable: true,
+							Fields: []*Field{
+								{
+									Name: []byte("details"),
+									Value: &Object{
+										Path:     []string{"details"},
+										Nullable: true,
+										Fields: []*Field{
+											{
+												Name: []byte("middlename"),
+												Value: &String{
+													Path:     []string{"middlename"},
+													Nullable: true,
+												},
+											},
+											{
+												Name: []byte("age"),
+												Value: &Integer{
+													Path:     []string{"age"},
+													Nullable: true,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		}
+
+		buf := &bytes.Buffer{}
+		_, err := newResolver(rCtx).ResolveGraphQLResponse(&Context{ctx: context.Background()}, node, nil, buf)
+		require.NoError(t, err)
+		assert.Contains(t, buf.String(), `"message":"Missing required federation dependency.","path":["me","details","age"]`)
+		assert.Contains(t, buf.String(), `"middlename":null`)
+		assert.Contains(t, buf.String(), `"age":null`)
+		ctrl.Finish()
+	})
+
 	t.Run("nested batching", testFn(func(t *testing.T, ctrl *gomock.Controller) (node *GraphQLResponse, ctx Context, expectedOutput string) {
 
 		productsService := mockedDS(t, ctrl,