EventSub websockets subscriptions failing

I’m creating an application that need access to some channel events (such as new followers, stream start, and so on). So I’m using EventSub with a Websockets (By the way thanks for adding this that’s exactly what I was searching a way around since a month) but as soon as I try to subscribe to an event the server answer me “websocket transport session does not exist or has already disconnected”.
Here is my process:

  • I connected with a Websocket to wss://eventsub-beta.wss.twitch.tv/ws
  • I received the following session welcome:
{
     "metadata": {
          "message_id":"85b68379-be44-4293-8fd5-09ba1f41cb7d",
          "message_type":"session_welcome",
          "message_timestamp":"2022-11-13T16:27:29.731303531Z"
     },
     "payload": {
          "session": {
               "id":"AQoQ055gnpskRUSxBmP0cpPAwBAB",
               "status":"connected",
               "connected_at":"2022-11-13T16:27:29.718001883Z",
               "keepalive_timeout_seconds":10,
               "reconnect_url":null
          }
     }
}
  • Then I sent a POST request to https://api.twitch.tv/helix/eventsub/subscriptions with:
POST /helix/eventsub/subscriptions HTTP/1.1
Accept-Ranges: bytes
Content-Length: 216
Host: api.twitch.tv
Content-Type: application/json
Authorization: Bearer {my oauth token}
Client-Id: {my public key}

{
  "type": "channel.follow",
  "version": "1",
  "condition": {
    "broadcaster_user_id": "{The broadcaster ID}"
  },
  "transport": {
    "method": "websocket",
    "session_id": "AQoQ055gnpskRUSxBmP0cpPAwBAB"
  }
}
  • But the server answer me:
HTTP/1.1 400 Bad
Connection: keep-alive
Content-Length: 119
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
Ratelimit-Limit: 800
Ratelimit-Remaining: 799
Ratelimit-Reset: 1668356850
Timing-Allow-Origin: https://www.twitch.tv
Date: Sun, 13 Nov 2022 16:27:30 GMT
X-Served-By: cache-bfi-kbfi7400085-BFI, cache-mrs10542-MRS
X-Cache: MISS, MISS
X-Cache-Hits: 0, 0
X-Timer: S1668356850.884626,VS0,VS0,VE215
Vary: Accept-Encoding
Strict-Transport-Security: max-age=300
Accept-Ranges: bytes

{
     "error":"Bad Request",
     "status":400,
     "message":"websocket transport session does not exist or has already disconnected"
}

Can you help me understand what I did wrong and why I can’t connect to EventSub ?

Sounds like you connected to eventsub websockets
got the session_id
disconnected from the websockets socetk
attempted to make subscriptions to a dead connection.

You need to connect to the WebSocket and within 10 seconds make subscription requests.
If you make no subscription requests within the 10 seconds then you will have been disconnected from the Twitch Socket Server.

The POST request was send on receiving the session_welcome message, so in less than 10 seconds. You can see it with the Twitch timestamps showing roughly 1 seconds between the session_welcome and the HTTP 400 response.
And I didn’t close by myself the websocket so I don’t understand why it fails :frowning:

My example here EventSub WebSockets with Implicit Auth Example is working as expected.

So it’s not a Twitch Side issue per sae.

So I can’t see anything wrong with what you have said is your order of operations.

The only thing I can think of is that your POST request to make a subscription. Is malformed. You declared a ContentType header of JSON but the message is not quite built nicely perhaps?

Not sure since this is just a Curl log. Are you just building command line CURL requests or a language to make HTTP calls?

1 Like

I’m using my own C# library to make the request, the same library I used with the Twitch API (appart from issue on my side with receiving chunked package it works as expected). But I see in the RFC 0016 it sends the session id as field “id” and not “session_id”. Do you have a complete example of the content sent to the server by the client (like that I could see what I am missing in my library) ?

The docs superseed the RFC. And one of the things that changed from the RFC is the move to use a session_id

You can use this live example and inspect the requests in chrome inspector - EventSub WebSockets with Implicit Auth Example

It does the following steps

  • implicit auth to get a token
  • connect to the websocket
  • get the session ID
  • create 6 subscriptions to the users own data

So your subscription payload looks correct, I’m just not sure if it’s correct.

So copying one of these “as curl” from my example

curl 'https://api.twitch.tv/helix/eventsub/subscriptions' \
  -H 'authority: api.twitch.tv' \
  -H 'accept: */*' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'authorization: Bearer REMOVED' \
  -H 'cache-control: no-cache' \
  -H 'client-id: hozgh446gdilj5knsrsxxz8tahr3koz' \
  -H 'content-type: application/json' \
  -H 'dnt: 1' \
  -H 'origin: https://barrycarlyon.github.io' \
  -H 'pragma: no-cache' \
  -H 'referer: https://barrycarlyon.github.io/' \
  -H 'sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Windows"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' \
  --data-raw '{"type":"channel.follow","version":1,"condition":{"broadcaster_user_id":"15185913"},"transport":{"method":"websocket","session_id":"AQoQgx4U5m9MSRWtUIghgSjI8BAB"}}' \
  --compressed

Thanks for your help I found out where my issue was. The issue I add was that the pong I sent back to the server weren’t masked, leading the server to close my connection. I changed that and now it works