Debugging "Error: Can't render headers after they are sent to the client."
I'm trying to figure out how to debug the above error.
This happens whenever I hit "Sign Out" on my Supabase Auth + Solid Start project. It's triggered by
setCookie
in my Supabase server client initialization function.
I'm not sure where to start on this one. I've been able to confirm that the problematic cookie is the Supabase Auth Token by logging out the name and the value when it errors: { name: 'sb-<project>-auth-token', value: '' }
Since the code is fairly small here, I'm just going to post it in full.
The HTML
signOutAction
:
createServerClient
:
This is part of a whole template that I've been working on, so I can also submit the whole thing to GitHub in PR form. 🙂14 Replies
I suspect that the response of the action is starting to stream before your code has a chance to access/update the cookie headers.
I ran into a similar situation
https://discord.com/channels/722131463138705510/1329164943878258739/1329546586572984391
It's only because I started to look at what was starting to stream in the response payload that I was able to figure out how to rearrange the code so that I could stop it from happening.
Oooh that is deviously difficult. By pulling out all other queries, I was able to get it to work. So clearly I've got the same kind of race condition you dealt with. I'll have to do a deep dive on the responses.
I'm not sure how I can slow this down though. Maybe the server-side client needs to be some kind of singleton.
This is what I'm working on: https://github.com/kjrocker/solid-start/blob/supabase-auth-example/examples/with-supabase-auth/src/util/supabase/actions.ts
GitHub
solid-start/examples/with-supabase-auth/src/util/supabase/actions.t...
SolidStart, the Solid app framework. Contribute to kjrocker/solid-start development by creating an account on GitHub.
My understanding of Supabase is tenuous at best but isn't
createServerClient
only needed for SSR? In other words sign out with the client directly?
Or is this a case of the cookies being buried inside the client request which the server client then turns around to use against supabase?Yeah that matches my thoughts as well.
All these actions hit Supabase's API anyway, they don't need to be server only
That said, client side actions are still useful for remote async operations, especially detached from a specific component because you'll often want to throw a
redirect
to the router; useNavigate
can get a bit glitchy in those situations.I'm not sure, but I think I ran into a problem with Vinxi not tree-shaking. I'm still testing it. Hopefully when the example repo is available to the world, people can learn from all these painful lessons 😆
I keep seeing errors in the console about AsyncLocalStorage not having a browser polyfill, which tells me something has gone very wrong. That, and it's just generally not stable, which usually means some kind of import error or library issue.
Moving things to the client simplified things a lot. I can't make an isomorphic client, so my actions need to be either client-side or server-side.
Now I'm having a new problem. This might be worth it's own thread.
This feels like the simplest thing. An async function returning an object, but it's fighting me.
I've tried that basic function in a
query
and as a createResource
fetcher, and neither Show
nor Suspense
give me consistent results.
I can tell it's not working because I have a Protected page that does work. So if I can be on the protected page, but not see the Sign Out, then something is wrong. And when I log user
it's always null/undefined.query
and as acreateResource
fetcher, and neitherShow
norSuspense
give me consistent results.
query
starts a transition while createResource
doesn't. So for query
Suspense
will only show the fallback
the very first time, while it does have a previous render under it.
For createResource
the Suspense
fallback
should show anytime it has a pending promise under it.
Have you done a console.log(user)
before return user
on the server side? (In case it's some weird serialization issue. I'm assuming you did a createEffect
log on the client side).I realized it probably didn't know the session had updated, because it was happening over in the supabase client and NOT within Solid's reactivity.
So I threw the Supabase session into a signal, subscribed to updates with supabases's built in helper, put that in a context, and moved all my actions that affect auth unto the client. That seems to have worked, and now my PR is live https://github.com/solidjs/solid-start/pull/1830 😄
GitHub
Supabase Auth Example by kjrocker · Pull Request #1830 · solidjs/so...
PR Checklist
Please check if your PR fulfills the following requirements:
Addresses an existing open issue: fixes #000
Tests for the changes have been added (for bug fixes / features)
What is t...
GitHub
solid-start/examples/with-supabase-auth/src/util/supabase/actions.t...
SolidStart, the Solid app framework. Contribute to kjrocker/solid-start development by creating an account on GitHub.
Did the linter complain without the
createEffect?
https://github.com/kjrocker/solid-start/blob/9b12e9fd54a4f448f34f108233efdc1fb1b56e73/examples/with-supabase-auth/src/util/supabase/session-context.tsx#L10-L18GitHub
solid-start/examples/with-supabase-auth/src/util/supabase/session-c...
SolidStart, the Solid app framework. Contribute to kjrocker/solid-start development by creating an account on GitHub.
I wasn't sure if I could force the actions to only run on the client. It does seem to work without it, but is there a better way to tell solid that the action is absolutely 100% client only?
The createEffect is taken right out of the supabase docs for SolidJS. https://supabase.com/docs/guides/getting-started/tutorials/with-solidjs#launch but it doesn't seem to be necessary.
Build a User Management App with SolidJS | Supabase Docs
Learn how to use Supabase in your SolidJS App.
"use client"
isn't a thing with SolidStart; action
and query
are client side router concepts; "use server" wraps the section under it in an RPC call.
out of the supabase docsIt seems a bit of React-ism.
createEffect
exists to leave the reactive graph; that code enters the reactive graph. createEffect
will run at least once in order to capture the reactive dependencies but in this case there aren't any.
As a control enhusiast I'd been inclined to just try:
Yeah it worked fine without it, so I removed it.
This is my first major project with Solid, and I'm coming from React, so the React-isms will inevitably leak in.
I think I've got everything working now. Thank for your help, and the Solid crash course. Now I just need someone to officially pick up the PR on Github. I assume someone has seen it, I'm not in a hurry. That said, any tips on next steps would be appreciated 🙂