Skip to content

useSuspenseQueries combine is called with pending/error results despite types implying data is always defined #10129

Closed

Description

@dolphinlmg

Describe the bug

The combine function in useSuspenseQueries has types that narrow data to always be defined (non-undefined), implying it should only be called when all queries have successfully resolved. However, combine is actually invoked via QueriesObserver.#combineResult in the #notify path even when query results are in pending or error state (e.g., after queryClient.resetQueries() or when a query errors out).

This causes a runtime TypeError because user code in combine reasonably accesses .data without null checks — as the types suggest it is safe to do so.

Your minimal, reproducible example

https://codesandbox.io/p/devbox/wonderful-pine-74lmth?workspaceId=ws_QZPvCEAK42bpnZ7RJHDjaX

Steps to reproduce

  1. Open the reproduction link above.
  2. Confirm that loading... is displayed on the initial load.
  3. Open the console in the Preview panel.
  4. In DevTools, select a query and trigger loading or trigger error. Or click the reset query button.
  5. Uncaught TypeError: Cannot read properties of undefined (reading 'full_name') error is thrown.

Expected behavior

One of the following:

  1. If combine is intended to only receive successful results, combine should not be called when any query result is in a non-success state. The observer notification path should skip the combine optimization and let the framework render path handle suspension or error boundaries.

  2. If combine is intended to handle all states, The type signature of combine in useSuspenseQueries should reflect that data can be undefined, matching the runtime behavior.

How often does this bug happen?

Always

Screenshots or Videos

Image

Platform

  • OS: macOS
  • Browser: Chrome
  • Version: 144.0.7559.97

Tanstack Query adapter

react-query

TanStack Query version

v5.90.21

TypeScript version

v5.8.3

Additional context

React Version: v19.2.14

Activity

  1. added a commit that references this issue on Feb 15, 2026
    c380c87
  2. added a commit that references this issue on Mar 10, 2026
    c96178b
  3. idirdev commented on Mar 11, 2026

    @idirdev

    This is a type/runtime mismatch. useSuspenseQueries types tell you that data is always defined inside combine, which makes sense for the suspense contract (component shouldnt render until all queries succeed). But in practice, combine runs through QueriesObserver.#combineResult which gets called even when query states change to pending or error (e.g. after queryClient.resetQueries()).

    So the types promise "data is always there" but the runtime doesnt enforce that during state transitions.

    The proper fix from the library side would be to skip calling combine when any query result isnt in success state. The observer notification should fall through to the suspense/error boundary handling instead of running combine with incomplete data.

    As a defensive workaround in the meantime:

    const result = useSuspenseQueries({
      queries: [...],
      combine: (results) => {
        if (results.some(r => r.status !== "success")) {
          return fallbackValue;
        }
        return results.map(r => r.data.full_name);
      },
    });

    Not ideal since the types say you shouldnt need this, but it prevents the runtime crash.

  4. added a commit that references this issue on Mar 25, 2026
    18e2744
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions