Skip to content

notFound() won't emit head data if __root has an async loader #6085

Closed

Description

@paperclover

Which project does this relate to?

Router

Describe the bug

when having a route's loader throw notFound, the not found page is rendered without head if the __root route's loader is not complete.

Your Example Website or App

https://github.com/paperclover/bug-reproductions/tree/tanstack-start-head-omitted

Steps to Reproduce the Bug or Issue

  • npm i
  • npm run dev
  • open http://localhost:3000, navigate to see css exists
  • open http://localhost:3000/not-found on new tab, no css

Expected behavior

to wait for __root's loader and render the head data.

Screenshots or Videos

No response

Platform

  • Router / Start Version: 1.130.2 / 1.135.2
  • OS: macOS
  • Browser: N/A
  • Browser Version: N/A
  • Bundler: vite
  • Bundler Version: ^7.1.7

Additional context

blocking a pr at work to properly handle some 404 cases where we are actually showing our ErrorComponent

Activity

  1. coderabbitai commented on Dec 12, 2025

    @coderabbitai
    Contributor

    📝 CodeRabbit Plan Mode

    Generate an implementation plan and prompts that you can use with your favorite coding agent.

    • Create Plan
    Examples

    🔗 Similar Issues

    Possible Duplicates

    Related Issues

    🔗 Related PRs

    #5495 - fix(react-router,solid-router): top-level Matches should use pendingComponent on root route [merged]
    #5500 - docs(beforeLoad & notFoundComponents): Add note regarding notFound error thrown from beforeLoad function [merged]
    #5518 - Solid hydration [merged]
    #5686 - fix(router-core): Catch errors thrown during hydrate [merged]
    #5890 - Fix: Route with server function as loader that throws notFound crashes route on HMR #5763 [merged]

    👤 Suggested Assignees


    🧪 Issue enrichment is currently in early access.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false
  2. paperclover commented on Dec 12, 2025

    @paperclover
    Author

    so i thought i could work around this by putting the root loader logic into beforeLoad, but now if that fails i hit #4684. so there isn't actually a way to perfectly work around this bug.

    in the work app this means that if one of the backend services are down, a fetch or db failure. and these do happen (but rarely) according to observability.

  3. schiller-manuel commented on Dec 13, 2025

    @schiller-manuel
    Collaborator

    your reproduction uses this in the route, which does not work on the client

    import { setTimeout } from "node:timers/promises";

    if you replace it it works as expected:

    diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx
    index 830ed0b..2235ac9 100644
    --- a/src/routes/__root.tsx
    +++ b/src/routes/__root.tsx
    @@ -9,7 +9,6 @@ import {
     import { DefaultCatchBoundary } from "../components/DefaultCatchBoundary.js";
     import { NotFound } from "../components/NotFound.js";
     import css from "../app.css?url";
    -import { setTimeout } from "node:timers/promises";
     
     export const Route = createRootRoute({
       head: () => ({
    @@ -32,7 +31,8 @@ export const Route = createRootRoute({
     
       // remove async loader to fix bug
       async loader() {
    -    await setTimeout(100); // causes bug
    +    await new Promise((resolve) => setTimeout(resolve, 100));
       },
  4. paperclover commented on Dec 13, 2025

    @paperclover
    Author

    oh shoot my bad. but changing it still causes the bug to reproduce, but only on the initial page load. this was what we were facing at work.

    if you replace it it works as expected:

    did you try doing a page refresh on it? that is the bug.

    the workaround for us is to add code on every notFound to call ensureQueryData for the query that we have in our root's loader, so that the root loader completes before notFound is thrown

  5. schiller-manuel commented on Dec 13, 2025

    @schiller-manuel
    Collaborator

    can reproduce this on initial page load, looking into it

  6. Eiley2 commented on Dec 14, 2025

    @Eiley2

    I think this issue might be related. Mind taking a look? #5960

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