Realtime and headers already sent, server port change

Hi, We’ve implemented Real-time using Redis on a Redwood project. However, we are getting an odd error locally when either debugging using console.log from services or making any file changes (e.g., updating service logic) that cause the server to rebuild.

The issue is that the server rebuilds but changes ports, and then the web no longer connects to the API, and we have to stop and run the yarn rw dev command again.

It only occurs only when we have an active subscription on a route from the web. If we navigate away from the page that has the subscription, this doesn’t happen.

Before the error, we are running on port 8910

web |   ➜  Local:   http://localhost:8910/
web |   ➜  Network: http://192.168.0.14:8910/

Once we make a change or have a service do a console.log we get the following and then a rebuild with new port

api | 06:34:23 🐛 Processing GraphQL Parameters done.
api | [change] \src\lib\realtime.js
api | Building...
web | node:_http_server:339
web |     throw new ERR_HTTP_HEADERS_SENT('write');
web |     ^
web |
web | Error: Cannot write headers after they are sent to the client
web |     at new NodeError (node:internal/errors:399:5)
web |     at ServerResponse.writeHead (node:_http_server:339:11)
web |     at ProxyServer.<anonymous> (C:\Users\saps1\Desktop\source_code_folder\Redwood.Metronic\node_modules\@redwoodjs\vite\dist\index.js:230:25)
web |     at ProxyServer.emit (file:///C:/Users/saps1/Desktop/source_code_folder/Redwood.Metronic/node_modules/vite/dist/node/chunks/dep-52909643.js:62222:28)
web |     at ClientRequest.proxyError (file:///C:/Users/saps1/Desktop/source_code_folder/Redwood.Metronic/node_modules/vite/dist/node/chunks/dep-52909643.js:63568:18)      
web |     at ClientRequest.emit (node:events:511:28)
web |     at Socket.socketErrorListener (node:_http_client:495:9)
web |     at Socket.emit (node:events:511:28)
web |     at emitErrorNT (node:internal/streams/destroy:151:8)
web |     at emitErrorCloseNT (node:internal/streams/destroy:116:3)
web |     at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
web |   code: 'ERR_HTTP_HEADERS_SENT'
web | }
web |
web | Node.js v20.0.0
web | yarn cross-env NODE_ENV=development rw-vite-dev  exited with code 1
api | Took 4364 ms
api | Debugger listening on ws://127.0.0.1:18911/ed2ddb05-e7cd-461c-b8b5-06ab641a8ca4
api | For help, see: https://nodejs.org/en/docs/inspector
api | Logs will be sent to the transport stream in the current development environment.
api | Importing Server Functions... 
api | /generateProjectDetails 2374 ms
api | ...Done importing in 2375 ms
api | GraphQL Yoga Server endpoint at graphql
api | GraphQL Yoga Server Health Check endpoint at graphql/health
api | GraphQL Yoga Server Readiness endpoint at graphql/readiness
api | 06:40:41 🌲 Server listening at http://[::]:8911
api | Server listening at http://[::]:8911/

I understand that having an open subscription is causing the issue, I’m familiar with this error from my PHP days. However is there a way to keep the route with subscription open, and have the server rebuild on the same port, then have the subscription auto-reconnect? Or what is another approach?

Hi @anthony-sap :wave:

I’m not sure I fully follow when you say it rebuilds on a different port. The web side runs on 8910 and then the API side runs on 8911. When the dev server detects a change the web will do any rebuilding using the vite dev server. For the API side the server should restart with the newly built code on the same 8911 port. Is this not what you’re seeing?

I’m happy to help more, especially if you see some undesirable behavior when it comes to having open subscriptions during dev server restarts.

Just to clarify:

  1. You have a page/cell/etc that calls useSubscription to subscribr to a message
  2. You do some developer on web or api side whilest dev server is running
  3. The dev server reloads due to change

And you want the subscription to be kept open and reconnect? So that a message response subscription ion complete is still handled?

That’s a use case I confess I never considered and imagine only happens in dev … and while . Typically I restart the server and then “do something” to re-subscribe to that message.

Perhaps a repo we can clone or a short video of the behavior you’re seeing could help us understand and replicate and then consider a fix?

Attached is a recording showing the issue. The service I modified isn’t queried from the page that is appearing ( Main UI Hidden).

https://www.dropbox.com/scl/fi/jkh1s81n2p051mmgh09gh/ioh66xRCHF.mp4?rlkey=hrp5c8vj1e3j6ibpooyp71s24&st=ynqcwt2b&dl=0 

This is the subscription in the component

const PROJECT_ACTION_SUBSCRIPTION = gql`
  subscription ListenForNewMessages($id: String!) {
    projectMessage(projectId: $id) {
      projectId
      action
      moduleId
      userStoryId
      errorMessage
      success
    }
  }
useSubscription(PROJECT_ACTION_SUBSCRIPTION, {
    variables: { id: projectId },
    onData: ({ data }) => {
      
    },
  })

In the video, you can see once I save the Service, then the headers error is reported, and then it rebuilds, and the front end stops working, I need to stop and rebuild to get going again. While this is only occurring in development, it is quite a slow and tedious process to work through changes and debug things as any change forces a complete rebuild each time.

Thanks for the video. Super helpful!

I know the team is working on some new hot reloading so I’ll share this experience.

I’ll have to think why this might happen.

Thanks @dthyresson I understand the issue, as mentioned, quite familiar with it in terms of PHP. From memory it was when the headers have been sent but then something is output to the response stream, then you send the response you actually want to send. .