Skip to content

fix(hydration): set dataUpdatedAt when pending query resolves before hydration#10610

Open
DORI2001 wants to merge 1 commit intoTanStack:mainfrom
DORI2001:fix/hydration-dateupdatedat-streamed-query
Open

fix(hydration): set dataUpdatedAt when pending query resolves before hydration#10610
DORI2001 wants to merge 1 commit intoTanStack:mainfrom
DORI2001:fix/hydration-dateupdatedat-streamed-query

Conversation

@DORI2001
Copy link
Copy Markdown

@DORI2001 DORI2001 commented Apr 29, 2026

Problem

When a query is dehydrated while still pending (streamed) but then resolves before hydration runs on the client, the hydration code transitions the query's status from 'pending' to 'success' — but it never updates dataUpdatedAt. It stays at 0.

This was introduced in #10444, which removed the query.fetch() call that previously set dataUpdatedAt as a side effect of resolving the promise.

Steps to reproduce: dehydrate a pending query (e.g. from a React Server Component), resolve it before sending the response, then hydrate on the client. query.state.dataUpdatedAt will be 0 instead of a real timestamp.

Fix

When the pending→success transition is applied (both in the setState path for existing queries and in the queryCache.build path for new queries), add dataUpdatedAt: dehydratedAt ?? Date.now().

dehydratedAt is the right value here — it records when the server serialized the resolved data, which is when the data was actually "updated". Falling back to Date.now() handles older dehydration payloads that don't include dehydratedAt.

Fixes #10603

Summary by CodeRabbit

  • Bug Fixes
    • Corrected timestamp information for queries restored from saved state, ensuring accurate data freshness tracking when recovering pending queries with resolved data.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

The change fixes hydration of queries that were pending during dehydration but resolved with data before hydration occurs. It now correctly sets dataUpdatedAt to dehydratedAt if available, otherwise Date.now(), preventing incorrect zero values.

Changes

Cohort / File(s) Summary
Hydration timestamp correction
packages/query-core/src/hydration.ts
Added logic to set dataUpdatedAt when hydrating a query with status pending but containing resolved data. Uses dehydratedAt timestamp if present, falls back to Date.now(). Ensures correct timestamp for streamed queries that resolve before hydration.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A query once pending, now holds resolved treasure,
Its timestamp was lost, but we fixed it with pleasure!
From dehydratedAt or the present we take,
No more zeros lingering—hydration's awake! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately describes the main fix: setting dataUpdatedAt during hydration of a pending query that resolves before hydration occurs.
Description check ✅ Passed Description provides clear problem statement, reproduction steps, fix explanation, and issue reference; matches template with completed problem and fix sections.
Linked Issues check ✅ Passed Changes fully address #10603 requirements: set dataUpdatedAt to dehydratedAt or Date.now() for pending-to-success transitions during hydration in both query update and build paths.
Out of Scope Changes check ✅ Passed All changes in hydration.ts are directly scoped to fixing the dataUpdatedAt issue for streamed queries; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/query-core/src/hydration.ts (1)

233-257: ⚠️ Potential issue | 🟠 Major

Older payloads can still skip pending→success upgrade in existing-query hydration.

When dehydratedAt is missing (older payload), hasNewerSyncData is always false (Line 237), and state.dataUpdatedAt is typically 0 for pending dehydrated state. That can keep the if on Line 239 false, so the transition at Line 253 and the timestamp fix at Line 256 never run for existing pending queries.

Suggested fix
-        const hasNewerSyncData =
-          syncData &&
-          // We only need this undefined check to handle older dehydration
-          // payloads that might not have dehydratedAt
-          dehydratedAt !== undefined &&
-          dehydratedAt > query.state.dataUpdatedAt
+        const hasNewerSyncData =
+          syncData &&
+          (dehydratedAt !== undefined
+            ? dehydratedAt > query.state.dataUpdatedAt
+            : // Older payloads: allow only pending->success upgrade for existing pending-without-data
+              state.status === 'pending' &&
+              query.state.status === 'pending' &&
+              query.state.data === undefined)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query-core/src/hydration.ts` around lines 233 - 257, The hydration
logic currently skips the pending→success upgrade when dehydratedAt is missing;
update the hasNewerSyncData computation to treat an undefined dehydratedAt as
“newer” for pending dehydrated states (e.g., set hasNewerSyncData = syncData &&
(dehydratedAt !== undefined ? dehydratedAt > query.state.dataUpdatedAt :
state.status === 'pending')). Then ensure when you call query.setState (same
block around query.setState and the pending→success branch) you still use
dataUpdatedAt: dehydratedAt ?? Date.now() so the timestamp correction runs even
if dehydratedAt was missing. This references the variables dehydratedAt,
syncData, state.status, state.dataUpdatedAt and the call to query.setState.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/query-core/src/hydration.ts`:
- Around line 233-257: The hydration logic currently skips the pending→success
upgrade when dehydratedAt is missing; update the hasNewerSyncData computation to
treat an undefined dehydratedAt as “newer” for pending dehydrated states (e.g.,
set hasNewerSyncData = syncData && (dehydratedAt !== undefined ? dehydratedAt >
query.state.dataUpdatedAt : state.status === 'pending')). Then ensure when you
call query.setState (same block around query.setState and the pending→success
branch) you still use dataUpdatedAt: dehydratedAt ?? Date.now() so the timestamp
correction runs even if dehydratedAt was missing. This references the variables
dehydratedAt, syncData, state.status, state.dataUpdatedAt and the call to
query.setState.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16c83652-7389-4dc8-9f6d-243d04125a88

📥 Commits

Reviewing files that changed from the base of the PR and between c5ab5a1 and 5a4c8db.

📒 Files selected for processing (1)
  • packages/query-core/src/hydration.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dataUpdatedAt incorrect for streamed queries that resolve before hydration

1 participant