TanStack / TanStack/query

useMutation invokes the next mutation's onSettled with the previous result

Open
#11,451 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
50.3k
Forks
4.2k
Avg merge
18h 25m
Merged PRs (30d)
200

Description

Describe the bug

When a per-call onSuccess or onError callback starts another mutation using the same useMutation instance, the second mutation's onSettled callback receives the first mutation's result and variables. It runs again with its own result when the second mutation finishes.

The reproduction logs only the callback registered for the second mutation:

[
  { "data": "result-1", "error": null, "variables": 1 },
  { "data": "result-2", "error": null, "variables": 2 }
]
Your minimal, reproducible example

https://yjdm6m.csb.app/ https://codesandbox.io/s/yjdm6m

Steps to reproduce
  1. Open the reproduction and click 1. Start second mutation in onSuccess.
  2. Observe that the second mutation's callback receives both result-1 / variables 1 and result-2 / variables 2.
  3. Click 2. Start second mutation in onError. The second mutation's callback similarly receives the first mutation's error and variables before receiving its own successful result.
  4. As a control, click 3. Sequential calls outside callbacks. This correctly logs only the second mutation's result once.
  5. Repeat with StrictMode enabled using the link at the bottom. The behavior is the same.
Expected behavior

The second mutation's onSettled callback should run only after that mutation settles, with its own data, error, and variables:

[
  { "data": "result-2", "error": null, "variables": 2 }
]

This is not a request to invoke per-call callbacks for every consecutive mutation. The issue is that the latest mutation's callback receives a different mutation's result.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform
  • macOS 26.5
  • Chromium 150.0.0.0, as reported by the browser's user agent
  • React / React DOM 19.2.1
  • Reproduces with StrictMode both enabled and disabled
Tanstack Query adapter

react-query

TanStack Query version

@tanstack/react-query@5.102.8 and @tanstack/query-core@5.102.8

TypeScript version

No response

Additional context

In additional local checks, the second mutation was held pending using a manually resolved Promise. Its onSettled callback still received the first mutation's result before the second mutation finished.

Omitting the second call's optional options argument also produces an internal TypeError, even though the user callback does not throw:

mutation.mutate(1, {
  onSuccess: () => {
    mutation.mutate(2)
  },
})
TypeError: Cannot read properties of undefined (reading 'onSettled')

In MutationObserver.#notify, this.#mutateOptions.onSettled is read after invoking onSuccess or onError. The nested mutate call replaces #mutateOptions, while the action's result and variables still belong to the first mutation.

The pending-Promise and omitted-options cases were checked separately locally; the shared sandbox contains the three button scenarios described above.

Codex assisted with preparing and verifying the reproduction.

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 in packages/query-core/src/mutationObserver.ts around MutationObserver.#notify (lines 240-293) and reproduce the nested-mutation scenarios from the linked CodeSandbox. Verify the second mutation's onSettled callback runs only with its own result, error, and variables, including when the second call omits options and when its promise remains pending.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
api, frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.