Skip to content

Errors thrown by middlewares do not get captured or reported #5263

Open

Description

@knackstedt

Describe the bug
If an unexpected error is thrown inside of a middleware, the error is completely ignored and not logged to any output stream or file. This makes errors impossible to debug without adding breakpoints into the dependency. This is obviously bad practice for a production system as rare errors will all get swept under the rug.

See here:

callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });

To Reproduce

Please fill the following code example:

Socket.IO server version: 4.8.x

Server

import { Server } from "socket.io";

const io = new Server(3000, {});
io.engine.use((req, res, next) => {
    if (!req.session?.hasSocketAccess) {
        return next(new Error("You do not have access"));
    }
    next();
});

io.on("connection", (socket) => {
  console.log(`connect ${socket.id}`);

  socket.on("disconnect", () => {
    console.log(`disconnect ${socket.id}`);
  });
});

Socket.IO client version: 4.8.x

Client

import { io } from "socket.io-client";

const socket = io("ws://localhost:3000/", {});

socket.on("connect", () => {
  console.log(`connect ${socket.id}`);
});

socket.on("disconnect", () => {
  console.log("disconnect");
});

Expected behavior
When the error is thrown in the middleware, it is able to be logged in some manner. Given that the current behavior tries to respond to the open HTTP connection and throws (yet another) error "Cannot set headers after they are sent to the client", I think socket.io needs to support being provided an error logger and if not provided, automatically log such errors to console.error.

Platform:

  • Device: Irrelevant.
  • OS: Irrelevant.

Additional context
I would have made a PR to resolve this, but I'm sure the current maintainers would have a preferred implementation for satisfying this need. The underlying "Cannot set headers after they are sent" error is probably an artifact of the implementation here, which might be resolved anyway by adding this logging support. I'm not totally sure what the intention is for that specific method, so I'll leave that up to the maintainers.

Activity

  1. knackstedt commented on Dec 17, 2024

    @knackstedt
    Author

    Edit: Also affects this closure.

    this._applyMiddlewares(req, res as unknown as ServerResponse, (err) => {

    (GitHub is currently quite laggy so I can't modify the original post :/ )

  2. cipherprofessor commented on Sep 24, 2026

    @cipherprofessor

    Confirmed this on current main. In packages/engine.io/lib/server.ts, _applyMiddlewares:

    const apply = (i) => {
      this.middlewares[i](req, res, (err?: any) => {
        if (err) return callback(err);
        ...
      });
    };

    The middleware call itself isn't wrapped in try/catch. If a registered middleware (engine.use(...), e.g. express-session, helmet, or a user's own auth middleware) throws synchronously instead of calling next(err), the exception propagates straight out of the http.Server 'request'/'upgrade' event listener — Node doesn't wrap those, so depending on the host setup it either crashes the process or gets silently swallowed somewhere above. Both call sites (handleRequest and handleUpgrade) go through this same unguarded path, matching @knackstedt's follow-up above.

    Proposed fix: wrap the middleware invocation in try/catch and route a caught exception into callback(err), the same path an explicit next(err) already takes.

    Before I put together a PR — is routing it through the existing callback(err)/connection_error path the behavior you'd want, or would you rather it surface differently (e.g. logged and re-thrown)? Happy to add a regression test alongside the existing ones in packages/engine.io/test/middlewares.js either way.

  3. added a commit that references this issue on Sep 27, 2026
    75e29b7
  4. added a commit that references this issue on Sep 30, 2026
    ac812ef
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    to triageWaiting to be triaged by a member of the team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions