PocketBase Realtime SSE Returns “Invalid realtime client” on Railway Custom Domain
agus08
FREEOP

a month ago

Hi Railway team,

I’m running PocketBase on Railway and PocketBase realtime SSE is failing only in production.

Service setup:

  • PocketBase deployed on Railway
  • Frontend deployed on Vercel
  • Railway service has 1 replica
  • PocketBase is started from Docker using pocketbase serve
  • Normal REST API requests work
  • /api/realtime SSE connection opens successfully

Issue:

PocketBase realtime subscription fails with:

{"message":"Invalid realtime client.","status":400,"data":{}}

Important detail:

This is reproducible outside the browser with curl, so it does not appear to be a Vercel, browser, or frontend CORS issue.

Reproduction:

  1. Open the SSE connection:

curl -i -N https://mydomain/api/realtime

  1. The server responds with HTTP 200 and emits a PocketBase realtime client id:

event: PB_CONNECT

data: {"clientId":""}

  1. While keeping that SSE connection open, immediately POST the subscription request to the same domain:

curl -i -X POST https://mydomain.com/api/realtime \

-H "Content-Type: application/json" \

-d '{"clientId":"","subscriptions":["sales/*"]}'

  1. Production returns:

HTTP 400

{"data":{},"message":"Invalid realtime client.","status":400}

Expected result:

PocketBase should accept the realtime subscription and return HTTP 204.

Comparison:

The exact same GET + POST flow works locally against PocketBase and returns HTTP 204.

Frontend detail:

The app uses the official PocketBase JavaScript SDK, not custom realtime fetch code.

SDK usage:

new PocketBase("https://mydomain.gussysalon.com")

pb.collection("sales").subscribe("*", handler, { filter })

pb.collection("sale_items").subscribe("*", handler, { filter })

pb.collection("sale_payments").subscribe("*", handler, { filter })

Auth is handled by PocketBase SDK authStore/Bearer token, not cookies.

Why I think this is Railway/proxy related:

The SSE GET succeeds and returns a clientId, but the immediate POST to the same /api/realtime endpoint with that exact clientId is rejected as invalid. It looks like the realtime client registered by the SSE connection is not visible when the subscription POST reaches the service.

Can you help check whether Railway’s proxy/custom-domain routing for SSE keeps the GET connection and POST request on the same backend process/context, and whether anything in the proxy could interfere with PocketBase realtime client validation?

$10 Bounty

1 Replies

Railway
BOT

a month ago

This thread has been opened as a bounty so the community can help solve it.

Status changed to Open Railway about 1 month ago


dawinz
FREE

a month ago

Since the SSE connection is established successfully, the proxy is at least allowing the stream through. The Invalid realtime client error usually means the clientId used in the POST isn't present in PocketBase's in-memory client registry.

A few things to check:

Make sure the GET /api/realtime and the POST /api/realtime are hitting the exact same origin (same scheme, host, and domain). Even a small difference (e.g. www vs non-www, or Railway URL vs custom domain) will create a different realtime session.

Verify that the clientId returned by the PB_CONNECT event is the same one you're sending in the POST. In your example it's redacted, so it's worth confirming the value isn't empty or being modified.

Try the same curl test against the default *.up.railway.app URL. If it works there but fails on the custom domain, that narrows the issue down to the custom-domain path rather than PocketBase itself.

One question: does the issue occur on both the Railway domain and your custom domain, or only on the custom domain? That comparison would help narrow it down quickly.


Welcome!

Sign in to your Railway account to join the conversation.

Loading...