-
Notifications
You must be signed in to change notification settings - Fork 541
Description
Bug Report
I can confirm that this issue originates in Supabase itself and not in my application code. I have also reviewed the documentation, GitHub Discussions, and Discord prior to submitting this report.
Summary
When using @supabase/supabase-js to send a broadcast message through Realtime without subscribing to the channel first, the client switches to the REST fallback endpoint (/realtime/v1/api/broadcast).
If no auth session or access token exists, this fallback layer generates an empty Authorization header (literally Authorization: \r\n).
Realtime responds with HTTP 500, even though manually calling this endpoint without any Authorization header works and returns 202.
Key observations
- The REST fallback always emits an
Authorizationheader, even when no token is present. - If the session is absent, the header is sent with no value, instead of being omitted.
global.headers.Authorizationis ignored by the REST fallback path.- Sending through WebSocket (after
subscribe()) or explicitly callingrealtime.setAuth(<token>)prevents the issue. - Making the same request manually with only the
apikeyheader succeeds.
Steps to Reproduce
Minimal server-side example (Service Role key, no user session):
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
auth: { autoRefreshToken: false, persistSession: false },
global: {
headers: {
apikey: process.env.SUPABASE_SERVICE_ROLE_KEY!,
Authorization: `Bearer ${process.env.SUPABASE_SERVICE_ROLE_KEY!}`,
},
},
}
)
const ch = supabase.channel(`channel:${'some-channel-id'}`, {
config: { private: true },
})
// No subscribe() call → triggers REST fallback
const ok = await ch.send({
type: 'broadcast',
event: 'new-message',
payload: { hello: 'world' },
})
console.log('send ok?', ok)Actual request produced by the client
POST /realtime/v1/api/broadcast
apikey: <service_role>
Authorization:
Content-Type: application/json
This results in:
HTTP 500 Internal Server Errorchannel.send()returns{ error: ... }without any detail about the 500 status.
Manual request that does work
await fetch(`${process.env.NEXT_PUBLIC_SUPABASE_URL}/realtime/v1/api/broadcast`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
apikey: process.env.SUPABASE_SERVICE_ROLE_KEY!,
},
body: JSON.stringify({
messages: [
{
topic: `channel:${'some-channel-id'}`,
event: 'new-message',
payload: { hello: 'world' },
private: true,
},
],
}),
})This returns 202 Accepted, and clients receive the broadcast.
Effective Workarounds
- Call
subscribe()and wait for theSUBSCRIBEDstatus before calling.send(). - Set the auth token explicitly via
supabase.realtime.setAuth(<jwt>)server-side.
Both prevent the empty Authorization header from being emitted.
Expected Behavior
- The REST fallback should omit the
Authorizationheader if no token is available, instead of sending a blank one. - Alternatively, the fallback should respect
global.headers.Authorizationwhen no auth session exists. - The Realtime REST endpoint should return 400 or 401 for empty/invalid headers instead of
500.
This scenario is fairly easy to run into, especially when .send() is called before .subscribe(). The silent 500 makes debugging difficult, since the status is only visible when inspecting raw network traffic.
Possible Fixes
- In
@supabase/supabase-js, avoid constructing anAuthorizationheader whenaccess_tokenis falsy (or use the value fromrealtime.getAuth()/global.headers). - In Realtime, return a proper client error instead of a server error when receiving an empty or invalid
Authorizationheader.
Library affected
supabase-js
Reproduction
No response
Steps to reproduce
No response
System Info
* **OS:** Windows 11 Home
* **supabase-js version:** 2.57.4
* **Node.js version:** 22.x
* **Environment:** server-side only, using Service Role key
* `auth.persistSession` is intentionally disabled, so the client has no session.Used Package Manager
npm
Logs
No response
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- Make sure this is a Supabase JS Library issue and not an issue with the Supabase platform. If it's a Supabase platform related bug, it should likely be reported to supabase/supabase instead.
- Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- The provided reproduction is a minimal reproducible example of the bug.