Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.97 Server fn headers are incomplete on initial load #3173

Closed
dotnize opened this issue Jan 15, 2025 · 20 comments
Closed

1.97 Server fn headers are incomplete on initial load #3173

dotnize opened this issue Jan 15, 2025 · 20 comments

Comments

@dotnize
Copy link

dotnize commented Jan 15, 2025

Which project does this relate to?

Start

Describe the bug

In Start 1.97, request headers and cookies from helper functions in @tanstack/start/server or vinxi/http (e.g. getWebRequest) are initially incomplete in server functions called in loaders/beforeLoad, breaking auth stuff.

When navigating/loading another page which will re-trigger beforeLoad, getWebRequest() returns the complete headers.

Your Example Website or App

https://github.com/dotnize/tss-webrequest-server-fn

https://tss-webrequest-server-fn.vercel.app/

Steps to Reproduce the Bug or Issue

  1. Clone repo and run dev
  2. Load any page. Headers returned and logged from server fn loader will be incomplete.
  3. Navigate to /test or /. Complete headers are now returned.

This is reproducible in dev and build.

Expected behavior

Complete headers are always returned by getWebRequest in 1.95.x

Screenshots or Videos

Screencast.From.2025-01-15.16-54-05.mp4

Platform

  • OS: [Linux]
  • Browser: [Chrome, Firefox]

Additional context

No response

@tannerlinsley
Copy link
Collaborator

I have an inkling of why this is happening. I’ll look into it today.

@einarno
Copy link

einarno commented Jan 17, 2025

For anyone depending on this 1.95.7 is the last version without this issue

@noah-wardlow
Copy link

seeing this too, but also on getHeaders() function. will also note 1.95.7 works as expected

@timoconnellaus
Copy link
Contributor

timoconnellaus commented Jan 19, 2025

I can't seem to get this on any version including 1.95.7. I cloned the test repo above and I see the issue with 1.95.7 and 1.97.3 (latest)

@prateekkumarweb
Copy link

Not just headers, even getCookies() returns undefined.

@tannerlinsley
Copy link
Collaborator

Cookies are powered by headers on the server. So that makes sense. I’m working on it!

@michaeldahl7
Copy link

I can't seem to get this on any version including 1.95.7. I cloned the test repo above and I see the issue with 1.95.7 and 1.97.3 (latest)

Make sure your package json doesn't have the ^ in front of the version number. If tanstack/start is ^1.95.7 it would update to 1.97.x due to the ^.

dotnize added a commit to dotnize/tanstarter that referenced this issue Jan 22, 2025
@dotnize dotnize changed the title 1.97 Server fn getWebRequest headers are incomplete on initial load 1.97 Server fn headers are incomplete on initial load Jan 22, 2025
@salamaashoush
Copy link

@tannerlinsley any update for this one, it prevents us from using the new React 19 and streaming stuff :D, let me know if I can help in any way

@tannerlinsley
Copy link
Collaborator

Should be fixed with #3256

@nick22985
Copy link

Hey @tannerlinsley,
It seems to still be an issue but now seems to work on the server but then gets refreshed on the client and gets rid of the data. Video attached just updated the example in this issue to replicate

https://gyazo.com/eb5086a22b05d4b28cf11f4c0eae9d74

The error:

react-dom-client.development.js:4128 
 Uncaught Error: Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:

- A server/client branch `if (typeof window !== 'undefined')`.
- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.
- Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.

It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.

https://react.dev/link/hydration-mismatch

  ...
    <RootDocument>
      <html>
        <head>
        <body>
          <OutletImpl>
            <Suspense fallback={null}>
              <MatchImpl matchId="/">
                <SafeFragment fallback={null}>
                  <SafeFragment getResetKey={function getResetKey} errorComponent={function ErrorComponent} ...>
                    <SafeFragment fallback={function fallback}>
                      <MatchInnerImpl matchId="/">
                        <Lazy>
                          <Home>
                            <div>
                              <a>
                              <pre>
-                               {"[\n  \"accept\",\n  \"accept-encoding\",\n  \"accept-language\",\n  \"cache-contro..."}
          ...

    at throwOnHydrationMismatch (react-dom-client.development.js:4128:11)
    at popHydrationState (react-dom-client.development.js:4267:9)
    at completeWork (react-dom-client.development.js:13679:17)
    at runWithFiberInDEV (react-dom-client.development.js:543:16)
    at completeUnitOfWork (react-dom-client.development.js:15179:19)
    at performUnitOfWork (react-dom-client.development.js:15061:11)
    at workLoopConcurrent (react-dom-client.development.js:15038:9)
    at renderRootConcurrent (react-dom-client.development.js:15013:15)
    at performWorkOnRoot (react-dom-client.development.js:14333:13)
    at performWorkOnRootViaSchedulerTask (react-dom-client.development.js:15931:7)

Sorry commented in the merge request by accident initially

@noah-wardlow
Copy link

also still seeing this issue

@tannerlinsley
Copy link
Collaborator

Lovely! Back to the drawing board.

@dotnize
Copy link
Author

dotnize commented Jan 28, 2025

react-dom-client.development.js:4128
Uncaught Error: Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client.

Are you using Route.useRouteContext() by any chance? This happened to me as well and the fix for me was replacing it with loader data.

dotnize/tanstarter@faf01aa

see #2010 (comment)

@tannerlinsley
Copy link
Collaborator

Hang on.. are you getting a hydration error because you're returning and rendering all of the headers? If so, this will not work. Some headers are distinctively different between SSR/document requests and client-side requests. This would most definitely result in a hydration error:

Image

prateekkumarweb added a commit to prateekkumarweb/twopi that referenced this issue Jan 28, 2025
@nick22985
Copy link

nick22985 commented Jan 28, 2025

react-dom-client.development.js:4128
Uncaught Error: Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client.

Are you using Route.useRouteContext() by any chance? This happened to me as well and the fix for me was replacing it with loader data.

dotnize/tanstarter@faf01aa

see #2010 (comment)

Was just using the example that was here https://github.com/dotnize/tss-webrequest-server-fn but it was also happening for me with better-auth it was both cookies and better-auth but now just better-auth doing it.

I am making a repo to replicate it now

I get the auth data by doing in __root.tsx which is just a server function. that gets Vinxi/http context. This was working prior to the update

	beforeLoad: async ({ context }) => {
		const auth = await context.queryClient.ensureQueryData(
			getAuthSessionOptions()
		)

		return { auth };
	},
import { createServerFn } from "@tanstack/start";
import { getEvent } from "vinxi/http";
import { type Auth } from "../auth/auth";
import { queryOptions } from "@tanstack/react-query";

export const getAuthSessionOptions = () =>
	queryOptions({
		queryKey: ['getAuthSession'],
		queryFn: () => getAuthSession()
	})

export const getAuthSession = createServerFn({ method: "GET" }).handler(
	async (): Promise<Auth> => {
		const event = getEvent();

		return event.context.auth;
	}
);

@nick22985
Copy link

Here is an example git repo. This was the same code I had before the update when it was working but cut down.
Recording of it: https://gyazo.com/f8d1dcf0fda71d3affe17682dfffc337
Example repo: https://github.com/nick22985/tss-test

@dotnize
Copy link
Author

dotnize commented Jan 28, 2025

Tried your repo and the issue for me was Route.useRouteContext(), which was returning undefined when the client hydrates. This was working fine back then and and I have no idea if this breaking was intentional but I think using that hook was probably a bad idea in the first place, as noted by @tannerlinsley's comment in #2010 (comment).

Instead of accessing the auth data via Route.useContext() directly, add a loader that re-returns the context data and use useLoaderData().

export const Route = createFileRoute('/')({
  component: Home,
+ loader: ({ context }) => {
+   return { auth: context.auth }
+ }
})

function Home() {
-  const { auth } = Route.useRouteContext();
+  const { auth } = Route.useLoaderData();

This makes the auth stuff work for me. Besides that, the other hydration error is probably caused by the theme script. suppressHydrationWarning in <html> works fine for me.

This is no longer related to headers but I've updated my repro for the useRouteContext issue. Replacing them with useLoaderData fixes it.

@SeanCassiere
Copy link
Member

@dotnize can this issue be closed?

@dotnize
Copy link
Author

dotnize commented Jan 28, 2025

Yeah if @nick22985 is still experiencing the hydration error even after the fix I mentioned then it's probably a different issue, not with headers.

Thank you!

@dotnize dotnize closed this as completed Jan 28, 2025
@nick22985
Copy link

Yes ty. Making it use the loader fixes the issue.

Thank you all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

No branches or pull requests