Skip to content

Commit d71da53

Browse files
yanghuidongclaude
andcommitted
fix: wait for async loaders before executing head functions
The bug: When loaders run asynchronously (stale-while-revalidate), loadRouteMatch returns immediately while the loader runs in the background. Promise.allSettled(inner.matchPromises) waits for loadRouteMatch promises, not the actual loader completion, causing head() to execute with undefined loaderData. The fix: After loadRouteMatch promises settle, explicitly wait for all loaderPromises to complete before executing head functions. This ensures loaderData is available when head() executes. Reproduction scenario: 1. Navigate to authenticated route (e.g., /article/123) 2. Delete auth cookie, reload (shows 'not found') 3. Login, redirect to dashboard 4. Click back button to /article/123 - Before fix: Article loads but title shows fallback (loaderData undefined) - After fix: Article loads with correct title (loaderData available) Fixes the issue identified in PR #6093 follow-up investigation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 71e560d commit d71da53

File tree

1 file changed

+10
-0
lines changed

1 file changed

+10
-0
lines changed

packages/router-core/src/load-matches.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,16 @@ export async function loadMatches(arg: {
900900
// Use allSettled to ensure all loaders complete regardless of success/failure
901901
const results = await Promise.allSettled(inner.matchPromises)
902902

903+
// Wait for async loaders to complete before executing head functions
904+
// loadRouteMatch may return immediately while loaders run asynchronously in the background
905+
// We need to wait for the actual loaderPromise, not just the loadRouteMatch promise
906+
await Promise.all(
907+
inner.matches.map((match) => {
908+
const currentMatch = inner.router.getMatch(match.id)
909+
return currentMatch?._nonReactive.loaderPromise || Promise.resolve()
910+
}),
911+
)
912+
903913
const failures = results
904914
// TODO when we drop support for TS 5.4, we can use the built-in type guard for PromiseRejectedResult
905915
.filter(

0 commit comments

Comments
 (0)