Skip to content

fix: remove index after _entities in the path#1306

Merged
ysmolski merged 3 commits into
masterfrom
yury/eng-8215-empty-path-in-error-response-for-query
Sep 25, 2025
Merged

fix: remove index after _entities in the path#1306
ysmolski merged 3 commits into
masterfrom
yury/eng-8215-empty-path-in-error-response-for-query

Conversation

@ysmolski

@ysmolski ysmolski commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

This index does not mean anything most likely to
the user, so we better remove it.

Summary by CodeRabbit

  • Bug Fixes
    • Error-path rewriting for federated/subgraph errors is now conditional and more accurate: nested/entity paths rewrite correctly, numeric indices handled as strings, and trailing "@" trimmed. Invalid/non-string segments are skipped (no empty placeholders), producing clearer actionable error messages without affecting successful responses.
  • Tests
    • Added tests covering diverse error-path rewrite scenarios to validate behavior when rewriting is enabled.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.

This index does not mean anything most likely to
the user, so we better remove it.
@coderabbitai

coderabbitai Bot commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Refactors error-path rewriting from a Loader method to a standalone function rewriteErrorPaths; mergeErrors now invokes it only when rewriteSubgraphErrorPaths is true. Updates path construction: builds a pathPrefix from fetchItem.ResponsePathElements (removes trailing "@"), rewrites segments after _entities, ignores numeric index immediately after _entities, converts string/number segments to strings, and skips non-string/non-number segments. Adds table-driven tests covering multiple rewrite scenarios.

Changes

Cohort / File(s) Summary
Resolver core logic
v2/pkg/engine/resolve/loader.go
Removes Loader.optionallyRewriteErrorPaths; adds top-level rewriteErrorPaths(fetchItem, values); mergeErrors now calls rewriteErrorPaths only when l.rewriteSubgraphErrorPaths is true. Path rewrite logic: build pathPrefix from fetchItem.ResponsePathElements (strip trailing "@"), start rewriting after "_entities", ignore a numeric index immediately after "_entities", convert string/number segments to string, and skip non-string/non-number segments (no empty placeholders).
Resolver tests
v2/pkg/engine/resolve/loader_test.go
Adds TestRewriteErrorPaths table-driven tests covering simple and nested rewrites, null/empty/no-entities cases, trailing and non-trailing "@" handling, and index handling; imports github.com/wundergraph/astjson.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately describes the main change—removing the numeric index after “_entities” in error paths—and follows Conventional Commits conventions, so it clearly communicates the primary fix to reviewers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
v2/pkg/engine/resolve/loader.go (2)

950-999: Entity index removal logic looks correct; clarify numeric segment stringification

The implementation correctly drops the numeric index immediately after _entities and preserves non‑trailing @. Note it also stringifies all subsequent numeric segments (e.g., 1 -> "1"), which matches the tests but should be called out in the function docs for future readers.

Apply this diff to document the behavior:

-// rewriteErrorPaths rewrites the path field for all the values.
+// rewriteErrorPaths rewrites GraphQL error "path" arrays for subgraph errors routed via _entities:
+// - Prefixes with fetchItem.ResponsePathElements (trailing "@" removed).
+// - Drops the numeric index immediately following "_entities".
+// - Converts all subsequent numeric segments to strings (e.g., 1 -> "1").
+// - Skips non-string/non-number segments.

990-994: Avoid per-error marshal/parse if this becomes hot; otherwise fine

json.Marshal + astjson.ParseBytesWithoutCache per error allocates. If this path ever gets hot, consider constructing the array via an arena-backed builder (passing in an arena or the resolvable) to avoid extra allocations. Not required for now.

v2/pkg/engine/resolve/loader_test.go (1)

1438-1539: Strong table-driven coverage; consider a couple more edge cases

Good coverage including nested fields, boolean segments, and @ handling. Optionally add:

  • Only _entities with no trailing segments (e.g., ["_entities"]).
  • _entities followed by an index only (e.g., ["_entities", 2]) to assert the result is exactly the fetch path prefix.

You could also use require.NoError for the parse copy to fail fast.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1542960 and 7d8034f.

📒 Files selected for processing (2)
  • v2/pkg/engine/resolve/loader.go (3 hunks)
  • v2/pkg/engine/resolve/loader_test.go (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Noroth
PR: wundergraph/graphql-go-tools#1262
File: v2/pkg/engine/datasource/grpc_datasource/json_builder.go:0-0
Timestamp: 2025-08-08T09:43:07.433Z
Learning: In v2/pkg/engine/datasource/grpc_datasource/json_builder.go, mergeEntities intentionally uses the loop index when calling indexMap.getResultIndex because the index map is type-aware, making per-type counters unnecessary under the current assumptions. Avoid suggesting per-type ordinal counters for this path in future reviews.
🧬 Code graph analysis (2)
v2/pkg/engine/resolve/loader_test.go (1)
v2/pkg/engine/resolve/fetch.go (1)
  • FetchItem (29-34)
v2/pkg/engine/resolve/loader.go (1)
v2/pkg/engine/resolve/fetch.go (1)
  • FetchItem (29-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and test (go 1.25 / ubuntu-latest)
  • GitHub Check: Build and test (go 1.25 / windows-latest)
  • GitHub Check: Build and test (go 1.25 / ubuntu-latest)
  • GitHub Check: Build and test (go 1.25 / windows-latest)
🔇 Additional comments (2)
v2/pkg/engine/resolve/loader_test.go (1)

12-13: Import addition: OK

astjson import for the new tests is appropriate.

v2/pkg/engine/resolve/loader.go (1)

752-754: Confirm default is false RewriteSubgraphErrorPaths is a bool with zero value false and only rewrites error paths when explicitly enabled in ResolverOptions.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
v2/pkg/engine/resolve/loader.go (3)

956-963: Avoid unnecessary allocation when building pathPrefix

You can re-slice instead of copy; no mutation occurs.

-	pathPrefix := make([]string, len(fetchItem.ResponsePathElements))
-	copy(pathPrefix, fetchItem.ResponsePathElements)
-	// remove the trailing @ in case we're in an array as it looks weird in the path
-	// errors, like fetches, are attached to objects, not arrays
-	if len(fetchItem.ResponsePathElements) != 0 && fetchItem.ResponsePathElements[len(fetchItem.ResponsePathElements)-1] == "@" {
-		pathPrefix = pathPrefix[:len(pathPrefix)-1]
-	}
+	pathPrefix := fetchItem.ResponsePathElements
+	// remove the trailing @ in case we're in an array as it looks weird in the path
+	// errors, like fetches, are attached to objects, not arrays
+	if n := len(pathPrefix); n > 0 && pathPrefix[n-1] == "@" {
+		pathPrefix = pathPrefix[:n-1]
+	}

976-976: Guard the string comparison with a type check

Slightly clearer and avoids converting non-strings to empty string before compare.

-			if unsafebytes.BytesToString(item.GetStringBytes()) == "_entities" {
+			if item.Type() == astjson.TypeString && unsafebytes.BytesToString(item.GetStringBytes()) == "_entities" {

982-984: Minor readability: use pathItems[j] instead of pathItems[i+1]

Same logic, less cognitive overhead inside the loop.

-					// If the item after _entities is an index (number), we should ignore it.
-					if j == i+1 && pathItems[i+1].Type() == astjson.TypeNumber {
+					// If the item after _entities is an index (number), we should ignore it.
+					if j == i+1 && pathItems[j].Type() == astjson.TypeNumber {
 						continue
 					}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d8034f and 9660bdb.

📒 Files selected for processing (2)
  • v2/pkg/engine/resolve/loader.go (3 hunks)
  • v2/pkg/engine/resolve/loader_test.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • v2/pkg/engine/resolve/loader_test.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Noroth
PR: wundergraph/graphql-go-tools#1262
File: v2/pkg/engine/datasource/grpc_datasource/json_builder.go:0-0
Timestamp: 2025-08-08T09:43:07.433Z
Learning: In v2/pkg/engine/datasource/grpc_datasource/json_builder.go, mergeEntities intentionally uses the loop index when calling indexMap.getResultIndex because the index map is type-aware, making per-type counters unnecessary under the current assumptions. Avoid suggesting per-type ordinal counters for this path in future reviews.
🧬 Code graph analysis (1)
v2/pkg/engine/resolve/loader.go (1)
v2/pkg/engine/resolve/fetch.go (1)
  • FetchItem (29-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and test (go 1.25 / windows-latest)
  • GitHub Check: Build and test (go 1.25 / ubuntu-latest)
  • GitHub Check: Build and test (go 1.25 / ubuntu-latest)
  • GitHub Check: Build and test (go 1.25 / windows-latest)
🔇 Additional comments (1)
v2/pkg/engine/resolve/loader.go (1)

752-754: No lingering references to optionalRewriteErrorPaths
Verified that optionalRewriteErrorPaths is removed and rewriteErrorPaths is only invoked in loader.go and its test.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
v2/pkg/engine/resolve/loader.go (2)

956-962: Confirm whether to drop the leading response-path element

renderErrorsInvalidInput removes the first element of ResponsePathElements before emitting a path (Lines 712-714). Should the same apply here to avoid including a root segment (e.g., "data")?

If yes, apply:

 pathPrefix := make([]string, len(fetchItem.ResponsePathElements))
 copy(pathPrefix, fetchItem.ResponsePathElements)
 // remove the trailing @ in case we're in an array as it looks weird in the path
 // errors, like fetches, are attached to objects, not arrays
 if len(fetchItem.ResponsePathElements) != 0 && fetchItem.ResponsePathElements[len(fetchItem.ResponsePathElements)-1] == "@" {
   pathPrefix = pathPrefix[:len(pathPrefix)-1]
 }
+// Optional: mirror renderErrorsInvalidInput behavior and drop the first element
+if len(pathPrefix) > 0 {
+  pathPrefix = pathPrefix[1:]
+}

996-1002: Avoid JSON round‑trip for building the new path (perf nit)

json.Marshal + ParseBytesWithoutCache allocates on every error. If this sits on a hot path, consider constructing the array via an arena (would require passing it in or keeping this as a method) to avoid the marshal/parse cycle.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9660bdb and 718b912.

📒 Files selected for processing (1)
  • v2/pkg/engine/resolve/loader.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
v2/pkg/engine/resolve/loader.go (1)
v2/pkg/engine/resolve/fetch.go (1)
  • FetchItem (29-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and test (go 1.25 / windows-latest)
  • GitHub Check: Build and test (go 1.25 / ubuntu-latest)
  • GitHub Check: Build and test (go 1.25 / ubuntu-latest)
  • GitHub Check: Build and test (go 1.25 / windows-latest)
🔇 Additional comments (3)
v2/pkg/engine/resolve/loader.go (3)

950-955: Behavior change note: numbers become strings in rewritten paths

Converting numeric path segments to strings changes the type shape of “path” entries. If clients rely on integer indices per GraphQL spec, call this out in release notes or make it configurable.


984-995: PR goal achieved: index after _entities is skipped

The loop correctly drops the numeric item immediately after "_entities" and rewrites subsequent segments.

If astjson numbers can exceed platform int, consider guarding against overflow when stringifying indices.


752-754: Feature-gated invocation correct; no stale references
optionallyRewriteErrorPaths is fully removed; rewriteErrorPaths is only called under the l.rewriteSubgraphErrorPaths flag in loader.go (tests invoke the helper directly for unit testing).

@ysmolski ysmolski merged commit 7d0586e into master Sep 25, 2025
11 checks passed
@ysmolski ysmolski deleted the yury/eng-8215-empty-path-in-error-response-for-query branch September 25, 2025 12:38
ysmolski pushed a commit that referenced this pull request Sep 25, 2025
🤖 I have created a release *beep* *boop*
---


##
[2.0.0-rc.229](v2.0.0-rc.228...v2.0.0-rc.229)
(2025-09-25)


### Bug Fixes

* remove index after _entities in the path
([#1306](#1306))
([7d0586e](7d0586e))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Version
  - Incremented to 2.0.0-rc.229.
- Bug Fixes
- Corrected path handling by removing a redundant “index” segment after
“_entities,” improving link resolution and navigation.
- Documentation
  - Added changelog entry for 2.0.0-rc.229 detailing the bug fix.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants