kysely icon indicating copy to clipboard operation
kysely copied to clipboard

Uncatchable connection errors in MS SQL

Open vicary opened this issue 10 months ago • 0 comments

This error happens specifically when connecting to a SQL Server with encryption disabled, without explicity set encrypt: false in Tedious.

It will results in two separate errors.

  1. [ESOCKET] socket hang up Error: Failed to connect to [REDACTED] - socket hang up

    {
        "code": "ESOCKET",
        "stack": [
            "Error: Failed to connect to [REDACTED] - socket hang up",
            "    at Connection2.socketError (REDACTED)",
            "    at Connection2.socketEnd (REDACTED)",
            "    at Socket.<anonymous> (REDACTED)",
            "    at Socket.emit (node:events:530:35)",
            "    at endReadableNT (node:internal/streams/readable:1698:12)",
            "    at process.processTicksAndRejections (node:internal/process/task_queues:90:21)"
        ]
    }
    

    The first error can be caught with propagateCreateError enabled in Tarn.

  2. [ESOCKET] Connection lost - unexpected end of message stream

    {
        "errorType": "Error",
        "errorMessage": "Connection lost - unexpected end of message stream",
        "code": "ESOCKET",
        "stack": [
            "Error: Connection lost - unexpected end of message stream",
            "    at Connection2.socketError ([REDACTED])",
            "    at [REDACTED]",
            "    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)"
        ]
    }
    

    The second error is uncatchable thus always exits the program.

Expected Results

Exactly one error is caught when the first connection is made via any query executions, for example:

try {
  await sql`SELECT 1`.execute(db);
} catch(e) {
  console.error("Connection Error", e);
}

console.log("This continue to work");

Workaround

Attach an error event listener to the Tedious connection.

const db = new Kysely({
  dialect: new MssqlDialect({
    tarn: {
      ...Tarn,
      options: {
        // This allows the first error to be caught
        propagateCreateError: true,
      },
    },
    tedious: {
      ...Tedious,
      connectionFactory: () => {
        const connection = new Tedious.Connection(...);

        connection.on("error", (error) => {
          // This catches/suppresses the second error
        });

        return connection;
      },
    },
  }),
});

vicary avatar Feb 05 '25 06:02 vicary