Skip to content

fix(incremental): avoid completing unreleased defer groups - #4859

Draft
duckki wants to merge 2 commits into
graphql:17.x.xfrom
duckki:unannounced-defer-completion
Draft

duckki wants to merge 2 commits into
graphql:17.x.xfrom
duckki:unannounced-defer-completion

Conversation

@duckki

@duckki duckki commented Sep 22, 2026

Copy link
Copy Markdown

Issue

A shared task can fail for both a released defer group and an unreleased nested defer group. For example:

{
  ... @defer(label: "R") {
    bad
  }
  ... @defer(label: "P") {
    slow
    ... @defer(label: "C") {
      bad
    }
  }
}

Given slow: String resolving to "ok" and bad: String! resolving to null, the initial result announces only R and P:

{
  "data": {},
  "pending": [
    { "id": "0", "path": [], "label": "R" },
    { "id": "1", "path": [], "label": "P" }
  ],
  "hasNext": true
}

However, the subsequent result currently includes a completion for a newly assigned ID:

{
  "incremental": [
    { "id": "1", "data": { "slow": "ok" } }
  ],
  "completed": [
    { "id": "0", "errors": ["..."] },
    { "id": "2", "errors": ["..."] },
    { "id": "1" }
  ],
  "hasNext": false
}

The nested C group was never announced, so no pending entry introduced ID "2".

The response specification requires each completion ID to refer to a pending entry from the initial result, an earlier subsequent result, or the same subsequent result:

This happens because WorkQueue.taskFailure emits a GROUP_FAILURE event for every defer group containing the shared failed task. That includes the unreleased C group. When the publisher maps that event, it assigns the group a new ID and emits its completion even though the group was never announced as pending.

Proposed solution

Record whether a failed defer group has been released before finishing it.

Continue calling finishGroupFailure for every affected group so that failed unreleased groups and their descendants are removed, cancelled, and accounted for. Emit a GROUP_FAILURE event only when the group was released.

With this change, the response completes R with its error and completes P after publishing slow. It does not assign an ID or emit a completion for the unreleased C group.

The tests cover both the public incremental response and the underlying WorkQueue events.

@vercel

vercel Bot commented Sep 22, 2026

Copy link
Copy Markdown

@duckki is attempting to deploy a commit to the The GraphQL Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@duckki
duckki marked this pull request as draft September 24, 2026 22:58
@duckki

duckki commented Sep 24, 2026

Copy link
Copy Markdown
Author

This is a real bug. But, the current proposed fix may not be the right fix. I'm validating a new fix.

  • GraphQL.js emits a failure completion for the unannounced child. This is the bug.
  • But, the spec does not allow dropping the child without justification.
  • So, a better solution might be delaying child's pending and completion (w/ failure) until its parent is released.

This branch has not been deployed

No deployments
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.

1 participant