Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

error caught, but exitCode is not zero #48756

Closed
loynoir opened this issue Jul 13, 2023 · 10 comments
Closed

error caught, but exitCode is not zero #48756

loynoir opened this issue Jul 13, 2023 · 10 comments

Comments

@loynoir
Copy link

loynoir commented Jul 13, 2023

Version

20.4.0

Platform

Docker ArchLinux 6.1.35-1-lts

Subsystem

No response

What steps will reproduce the bug?

      deepStrictEqual(
        await getExitCode({ ip, code: 'icmp-host-unreachable', nextTickLookup: false }),
        { caught: true, exitCode: 0 },
      )

      deepStrictEqual(
        await getExitCode({ ip, code: 'blackhole', nextTickLookup: false }),
        // unexpected
        { caught: true, exitCode: 1 },
      )
full code
import { deepStrictEqual } from 'node:assert'
import { spawnSync } from 'node:child_process'
import { request } from 'node:http'
import { nextTick } from 'node:process'
import { isMainThread } from 'node:worker_threads'
import { describe, it } from 'node:test'

// Replace with a v4 ip, 
// with http server on its 80 port.
// During the test, 
// this ip is unreachable.
const TEST_CONTAINER_IP = 'xxx.xxx.xxx.xxx'

function simulateBadNetwork(code: '' | 'icmp-host-unreachable' | 'blackhole', ip: string, want: boolean): void {
  switch (code) {
    case 'icmp-host-unreachable':
      if (want) {
        spawnSync('sudo', [
          'iptables',
          '-A',
          'OUTPUT',
          '-d',
          `${ip}/32`,
          '-m',
          'comment',
          '--comment',
          'x_test',
          '-j',
          'REJECT',
          '--reject-with',
          'icmp-host-unreachable',
        ])
      } else {
        spawnSync('sudo', [
          'iptables',
          '-D',
          'OUTPUT',
          '-d',
          `${ip}/32`,
          '-m',
          'comment',
          '--comment',
          'x_test',
          '-j',
          'REJECT',
          '--reject-with',
          'icmp-host-unreachable',
        ])
      }
      break
    case 'blackhole':
      if (want) {
        spawnSync('sudo', ['ip', 'route', 'add', 'blackhole', `${ip}`])
      } else {
        spawnSync('sudo', ['ip', 'route', 'del', 'blackhole', `${ip}`])
      }
      break
    default:
      break
  }
}

// eslint-disable-next-line @typescript-eslint/no-floating-promises
describe(request.name, { concurrency: true }, () => {
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
  it(request.name, { concurrency: true }, async () => {
    interface WorkerData {
      ip: string
      code: Parameters<typeof simulateBadNetwork>[0]
      nextTickLookup: boolean
    }

    if (isMainThread) {
      const { Worker } = await import('node:worker_threads')

      async function getExitCode(workerData: WorkerData): Promise<{ caught: boolean; exitCode: number }> {
        return await new Promise((resolve, _reject) => {
          let caught = false

          const worker = new Worker(new URL(import.meta.url), { workerData, stdout: true, stderr: true })
          worker.on('message', (_value) => {
            caught = true
          })
          worker.on('exit', (exitCode) => {
            simulateBadNetwork(workerData.code, workerData.ip, false)
            resolve({ caught, exitCode })
          })
        })
      }

      const ip = TEST_CONTAINER_IP

      deepStrictEqual(
        await getExitCode({ ip, code: '', nextTickLookup: false }),
        { caught: false, exitCode: 0 },
      )
      deepStrictEqual(
        await getExitCode({ ip, code: 'icmp-host-unreachable', nextTickLookup: false }),
        { caught: true, exitCode: 0 },
      )

      deepStrictEqual(
        await getExitCode({ ip, code: 'blackhole', nextTickLookup: false }),
        // unexpected
        { caught: true, exitCode: 1 },
      )

      deepStrictEqual(
        await getExitCode({ ip, code: '', nextTickLookup: true }),
        { caught: false, exitCode: 0 },
      )
      deepStrictEqual(
        await getExitCode({ ip, code: 'icmp-host-unreachable', nextTickLookup: true }),
        { caught: true, exitCode: 0 },
      )
      deepStrictEqual(
        await getExitCode({ ip, code: 'blackhole', nextTickLookup: true }),
        { caught: true, exitCode: 0 },
      )
    } else {
      const workerData = (await import('node:worker_threads')).workerData as WorkerData
      const { parentPort } = await import('node:worker_threads')

      try {
        await new Promise<void>((resolve, reject) => {
          const clientRequest = request('http://example.com', {
            lookup(_hostname, _options, callback) {
              if (workerData.nextTickLookup) {
                nextTick(() => {
                  simulateBadNetwork(workerData.code, workerData.ip, true)
                  callback(null, [{ address: workerData.ip, family: 4 }])
                })
              } else {
                simulateBadNetwork(workerData.code, workerData.ip, true)
                callback(null, [{ address: workerData.ip, family: 4 }])
              }
            },
          })

          const cleanup = (): void => {
            clientRequest.removeListener('error', onError)
            clientRequest.removeListener('close', onClose)
          }

          const onError = (err: Error): void => {
            cleanup()
            reject(new Error(undefined, { cause: err }))
          }

          const onClose = (): void => {
            cleanup()
            resolve()
          }

          clientRequest.addListener('error', onError)
          clientRequest.addListener('close', onClose)

          clientRequest.end()
        })
      } catch (e) {
        parentPort?.postMessage('caught')
      }
    }
  })
})

How often does it reproduce? Is there a required condition?

  • ip blackhole like behavior

What is the expected behavior? Why is that the expected behavior?

{ caught: true, exitCode: 0 }

What do you see instead?

{ caught: true, exitCode: 1 }

Additional information

No response

@bnoordhuis
Copy link
Member

Quoting the issue template:

What is the expected behavior? Why is that the expected behavior?

You say "what" but not "why". I've looked at your example and it doesn't explain it either.

@loynoir
Copy link
Author

loynoir commented Jul 14, 2023

@bnoordhuis

You say "what" but not "why". I've looked at your example and it doesn't explain it either.

Basically, the example is something like below

try {
  await fn(testOptionFromMatrix)
} catch(e) {}

I think nobody expect above code exitCode is not zero, so does not need explaination.


Besides,

  • When simulate reject-with-icmp-host-unreachable, error is caught, and exitCode is expected 0.

  • When simulate ip-blackhole, error is caught, but exitCode is unexpected 1.

Current behavior is also not identical.


If current behavior is expected, please provide code, showing how to get { caught: true, exitCode: 0 } when simulate ip-blackhole. Thanks.

@bnoordhuis
Copy link
Member

bnoordhuis commented Jul 14, 2023

What exceptions / error messages do the two code paths test cases produce on your system?

edit: edited for clarity

@loynoir
Copy link
Author

loynoir commented Jul 14, 2023

full code
/* global console */
import { deepStrictEqual } from 'node:assert'
import { spawnSync } from 'node:child_process'
import { request } from 'node:http'
import { env, nextTick } from 'node:process'
import { inspect } from 'node:util'
import { isMainThread } from 'node:worker_threads'
import { describe, it } from 'node:test'

// Env TEST_CONTAINER_IP with a v4 ip,
// with http server on its 80 port.
// During the test,
// this ip is unreachable.
const TEST_CONTAINER_IP = env['TEST_CONTAINER_IP']

function simulateBadNetwork(code: '' | 'icmp-host-unreachable' | 'blackhole', ip: string, want: boolean): void {
  switch (code) {
    case 'icmp-host-unreachable':
      if (want) {
        spawnSync('sudo', [
          'iptables',
          '-A',
          'OUTPUT',
          '-d',
          `${ip}/32`,
          '-m',
          'comment',
          '--comment',
          'x_test',
          '-j',
          'REJECT',
          '--reject-with',
          'icmp-host-unreachable',
        ])
      } else {
        spawnSync('sudo', [
          'iptables',
          '-D',
          'OUTPUT',
          '-d',
          `${ip}/32`,
          '-m',
          'comment',
          '--comment',
          'x_test',
          '-j',
          'REJECT',
          '--reject-with',
          'icmp-host-unreachable',
        ])
      }
      break
    case 'blackhole':
      if (want) {
        spawnSync('sudo', ['ip', 'route', 'add', 'blackhole', `${ip}`])
      } else {
        spawnSync('sudo', ['ip', 'route', 'del', 'blackhole', `${ip}`])
      }
      break
    default:
      break
  }
}

// eslint-disable-next-line @typescript-eslint/no-floating-promises
describe(request.name, { concurrency: true }, () => {
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
  it(request.name, { concurrency: true }, async () => {
    interface WorkerData {
      ip: string
      code: Parameters<typeof simulateBadNetwork>[0]
      nextTickLookup: boolean
    }

    if (isMainThread) {
      const { Worker } = await import('node:worker_threads')

      async function getExitCode(workerData: WorkerData): Promise<{ caught: boolean; exitCode: number }> {
        return await new Promise((resolve, _reject) => {
          let caught = false
          const msg: any[] = []

          const worker = new Worker(new URL(import.meta.url), { workerData, stdout: true, stderr: true })
          worker.on('message', (value) => {
            caught = true
            msg.push(value)
          })
          worker.on('exit', (exitCode) => {
            simulateBadNetwork(workerData.code, workerData.ip, false)
            resolve({ caught, exitCode })

            console.warn({ workerData, caught, exitCode, msg })
          })
        })
      }

      if (TEST_CONTAINER_IP === undefined) throw new Error()
      const ip = TEST_CONTAINER_IP

      deepStrictEqual(
        await getExitCode({ ip, code: '', nextTickLookup: false }),
        { caught: false, exitCode: 0 },
      )
      deepStrictEqual(
        await getExitCode({ ip, code: 'icmp-host-unreachable', nextTickLookup: false }),
        { caught: true, exitCode: 0 },
      )

      deepStrictEqual(
        await getExitCode({ ip, code: 'blackhole', nextTickLookup: false }),
        // unexpected
        { caught: true, exitCode: 1 },
      )

      deepStrictEqual(
        await getExitCode({ ip, code: '', nextTickLookup: true }),
        { caught: false, exitCode: 0 },
      )
      deepStrictEqual(
        await getExitCode({ ip, code: 'icmp-host-unreachable', nextTickLookup: true }),
        { caught: true, exitCode: 0 },
      )
      deepStrictEqual(
        await getExitCode({ ip, code: 'blackhole', nextTickLookup: true }),
        { caught: true, exitCode: 0 },
      )
    } else {
      const workerData = (await import('node:worker_threads')).workerData as WorkerData
      const { parentPort } = await import('node:worker_threads')

      try {
        await new Promise<void>((resolve, reject) => {
          const clientRequest = request('http://example.com', {
            lookup(_hostname, _options, callback) {
              if (workerData.nextTickLookup) {
                nextTick(() => {
                  simulateBadNetwork(workerData.code, workerData.ip, true)
                  callback(null, [{ address: workerData.ip, family: 4 }])
                })
              } else {
                simulateBadNetwork(workerData.code, workerData.ip, true)
                callback(null, [{ address: workerData.ip, family: 4 }])
              }
            },
          })

          const cleanup = (): void => {
            clientRequest.removeListener('error', onError)
            clientRequest.removeListener('close', onClose)
          }

          const onError = (err: Error): void => {
            cleanup()
            reject(new Error(undefined, { cause: err }))
          }

          const onClose = (): void => {
            cleanup()
            resolve()
          }

          clientRequest.addListener('error', onError)
          clientRequest.addListener('close', onClose)

          clientRequest.end()
        })
      } catch (e) {
        parentPort?.postMessage(inspect(e))
      }
    }
  })
})
full log
(node:413482) ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TAP version 13
{
  workerData: { ip: '{{TEST_CONTAINER_IP}}', code: '', nextTickLookup: false },
  caught: false,
  exitCode: 0,
  msg: []
}
{
  workerData: {
    ip: '{{TEST_CONTAINER_IP}}',
    code: 'icmp-host-unreachable',
    nextTickLookup: false
  },
  caught: true,
  exitCode: 0,
  msg: [
    'Error\n' +
      '    at ClientRequest.onError (/path/to/reproduce.ts:154:20)\n' +
      '    at ClientRequest.emit (node:events:512:28)\n' +
      '    at Socket.socketErrorListener (node:_http_client:495:9)\n' +
      '    at Socket.emit (node:events:512:28)\n' +
      '    at emitErrorNT (node:internal/streams/destroy:151:8)\n' +
      '    at emitErrorCloseNT (node:internal/streams/destroy:116:3)\n' +
      '    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {\n' +
      '  [cause]: Error: connect EHOSTUNREACH {{TEST_CONTAINER_IP}}:80\n' +
      '      at __node_internal_captureLargerStackTrace (node:internal/errors:496:5)\n' +
      '      at __node_internal_exceptionWithHostPort (node:internal/errors:671:12)\n' +
      '      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1592:16) {\n' +
      '    errno: -113,\n' +
      "    code: 'EHOSTUNREACH',\n" +
      "    syscall: 'connect',\n" +
      "    address: '{{TEST_CONTAINER_IP}}',\n" +
      '    port: 80\n' +
      '  }\n' +
      '}'
  ]
}
{
  workerData: { ip: '{{TEST_CONTAINER_IP}}', code: 'blackhole', nextTickLookup: false },
  caught: true,
  exitCode: 1,
  msg: [
    'Error\n' +
      '    at ClientRequest.onError (/path/to/reproduce.ts:154:20)\n' +
      '    at ClientRequest.emit (node:events:512:28)\n' +
      '    at Socket.socketCloseListener (node:_http_client:468:11)\n' +
      '    at Socket.emit (node:events:524:35)\n' +
      '    at TCP.<anonymous> (node:net:334:12) {\n' +
      '  [cause]: Error: socket hang up\n' +
      '      at connResetException (node:internal/errors:720:14)\n' +
      '      at Socket.socketCloseListener (node:_http_client:468:25)\n' +
      '      at Socket.emit (node:events:524:35)\n' +
      '      at TCP.<anonymous> (node:net:334:12) {\n' +
      "    code: 'ECONNRESET'\n" +
      '  }\n' +
      '}'
  ]
}
{
  workerData: { ip: '{{TEST_CONTAINER_IP}}', code: '', nextTickLookup: true },
  caught: false,
  exitCode: 0,
  msg: []
}
{
  workerData: {
    ip: '{{TEST_CONTAINER_IP}}',
    code: 'icmp-host-unreachable',
    nextTickLookup: true
  },
  caught: true,
  exitCode: 0,
  msg: [
    'Error\n' +
      '    at ClientRequest.onError (/path/to/reproduce.ts:154:20)\n' +
      '    at ClientRequest.emit (node:events:512:28)\n' +
      '    at Socket.socketErrorListener (node:_http_client:495:9)\n' +
      '    at Socket.emit (node:events:512:28)\n' +
      '    at emitErrorNT (node:internal/streams/destroy:151:8)\n' +
      '    at emitErrorCloseNT (node:internal/streams/destroy:116:3)\n' +
      '    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {\n' +
      '  [cause]: Error: connect EHOSTUNREACH {{TEST_CONTAINER_IP}}:80\n' +
      '      at __node_internal_captureLargerStackTrace (node:internal/errors:496:5)\n' +
      '      at __node_internal_exceptionWithHostPort (node:internal/errors:671:12)\n' +
      '      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1592:16) {\n' +
      '    errno: -113,\n' +
      "    code: 'EHOSTUNREACH',\n" +
      "    syscall: 'connect',\n" +
      "    address: '{{TEST_CONTAINER_IP}}',\n" +
      '    port: 80\n' +
      '  }\n' +
      '}'
  ]
}
{
  workerData: { ip: '{{TEST_CONTAINER_IP}}', code: 'blackhole', nextTickLookup: true },
  caught: true,
  exitCode: 0,
  msg: [
    'Error\n' +
      '    at ClientRequest.onError (/path/to/reproduce.ts:154:20)\n' +
      '    at ClientRequest.emit (node:events:512:28)\n' +
      '    at Socket.socketErrorListener (node:_http_client:495:9)\n' +
      '    at Socket.emit (node:events:512:28)\n' +
      '    at emitErrorNT (node:internal/streams/destroy:151:8)\n' +
      '    at emitErrorCloseNT (node:internal/streams/destroy:116:3)\n' +
      '    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {\n' +
      '  [cause]: Error: connect EINVAL {{TEST_CONTAINER_IP}}:80 - Local (0.0.0.0:0)\n' +
      '      at __node_internal_captureLargerStackTrace (node:internal/errors:496:5)\n' +
      '      at __node_internal_exceptionWithHostPort (node:internal/errors:671:12)\n' +
      '      at internalConnect (node:net:1087:16)\n' +
      '      at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)\n' +
      '      at emitLookup (node:net:1478:9)\n' +
      '      at <anonymous> (/path/to/reproduce.ts:138:19)\n' +
      '      at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {\n' +
      '    errno: -22,\n' +
      "    code: 'EINVAL',\n" +
      "    syscall: 'connect',\n" +
      "    address: '{{TEST_CONTAINER_IP}}',\n" +
      '    port: 80\n' +
      '  }\n' +
      '}'
  ]
}
# Subtest: request
    # Subtest: request
    ok 1 - request
      ---
      duration_ms: 1086.378818
      ...
    1..1
ok 1 - request
  ---
  duration_ms: 1087.921034
  type: 'suite'
  ...
1..1
# tests 1
# suites 1
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 0.215729

@bnoordhuis
Copy link
Member

  '  [cause]: Error: connect EINVAL {{TEST_CONTAINER_IP}}:80 - Local (0.0.0.0:0)\n' +

This here is likely your problem. It gets kind of obfuscated due to the stitched-together async stack trace but that's a synchronous "bad parameter" exception, not a runtime network error like the others.

@loynoir
Copy link
Author

loynoir commented Jul 14, 2023

@bnoordhuis

That one is { caught: true, exitCode: 0 }, not a problem.

  caught: true,
  exitCode: 0,
  msg: [
...
      '  [cause]: Error: connect EINVAL {{TEST_CONTAINER_IP}}:80 - Local (0.0.0.0:0)\n' +

The problematice one { caught: true, exitCode: 1 } log is below

{
  workerData: { ip: '{{TEST_CONTAINER_IP}}', code: 'blackhole', nextTickLookup: false },
  caught: true,
  exitCode: 1,
  msg: [
    'Error\n' +
      '    at ClientRequest.onError (/path/to/reproduce.ts:154:20)\n' +
      '    at ClientRequest.emit (node:events:512:28)\n' +
      '    at Socket.socketCloseListener (node:_http_client:468:11)\n' +
      '    at Socket.emit (node:events:524:35)\n' +
      '    at TCP.<anonymous> (node:net:334:12) {\n' +
      '  [cause]: Error: socket hang up\n' +
      '      at connResetException (node:internal/errors:720:14)\n' +
      '      at Socket.socketCloseListener (node:_http_client:468:25)\n' +
      '      at Socket.emit (node:events:524:35)\n' +
      '      at TCP.<anonymous> (node:net:334:12) {\n' +
      "    code: 'ECONNRESET'\n" +
      '  }\n' +
      '}'
  ]
}

@loynoir
Copy link
Author

loynoir commented Jul 14, 2023

Even more weird.

Extract code from worker based test, now error is not caught, which does caught within worker.

I may create a reproduce repo when have time, seems like a complicated problem.

code not using worker
import { spawnSync } from 'node:child_process'
import { request } from 'node:http'
import { env, nextTick } from 'node:process'
import { inspect } from 'node:util'

const TEST_CONTAINER_IP = env['TEST_CONTAINER_IP']

function simulateBadNetwork(code: '' | 'icmp-host-unreachable' | 'blackhole', ip: string, want: boolean): void {
  switch (code) {
    case 'icmp-host-unreachable':
      if (want) {
        spawnSync('sudo', [
          'iptables',
          '-A',
          'OUTPUT',
          '-d',
          `${ip}/32`,
          '-m',
          'comment',
          '--comment',
          'x_test',
          '-j',
          'REJECT',
          '--reject-with',
          'icmp-host-unreachable',
        ])
      } else {
        spawnSync('sudo', [
          'iptables',
          '-D',
          'OUTPUT',
          '-d',
          `${ip}/32`,
          '-m',
          'comment',
          '--comment',
          'x_test',
          '-j',
          'REJECT',
          '--reject-with',
          'icmp-host-unreachable',
        ])
      }
      break
    case 'blackhole':
      if (want) {
        spawnSync('sudo', ['ip', 'route', 'add', 'blackhole', `${ip}`])
      } else {
        spawnSync('sudo', ['ip', 'route', 'del', 'blackhole', `${ip}`])
      }
      break
    default:
      break
  }
}

{
  interface WorkerData {
    ip: string
    code: Parameters<typeof simulateBadNetwork>[0]
    nextTickLookup: boolean
  }

  if (TEST_CONTAINER_IP === undefined) throw new Error()
  const ip = TEST_CONTAINER_IP

  const workerData: WorkerData = { ip, code: 'blackhole', nextTickLookup: false }

  try {
    await new Promise<void>((resolve, reject) => {
      const clientRequest = request('http://example.com', {
        lookup(_hostname, _options, callback) {
          if (workerData.nextTickLookup) {
            nextTick(() => {
              simulateBadNetwork(workerData.code, workerData.ip, true)
              callback(null, [{ address: workerData.ip, family: 4 }])
            })
          } else {
            simulateBadNetwork(workerData.code, workerData.ip, true)
            callback(null, [{ address: workerData.ip, family: 4 }])
          }
        },
      })

      const cleanup = (): void => {
        clientRequest.removeListener('error', onError)
        clientRequest.removeListener('close', onClose)
      }

      const onError = (err: Error): void => {
        cleanup()
        reject(new Error(undefined, { cause: err }))
      }

      const onClose = (): void => {
        cleanup()
        resolve()
      }

      clientRequest.addListener('error', onError)
      clientRequest.addListener('close', onClose)

      clientRequest.end()
    })
  } catch (e) {
    console.warn({ workerData, caught: true, msg: [inspect(e)] })
  }
}
log not using worker
(node:452690) ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
node:events:490
      throw er; // Unhandled 'error' event
      ^

Error: connect EINVAL {{TEST_CONTAINER_IP}}:80 - Local (0.0.0.0:0)
    at __node_internal_captureLargerStackTrace (node:internal/errors:496:5)
    at __node_internal_exceptionWithHostPort (node:internal/errors:671:12)
    at internalConnect (node:net:1087:16)
    at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)
    at emitLookup (node:net:1478:9)
    at lookup (/path/to/reproduce.ts:80:13)
    at emitLookup (node:net:1402:5)
    at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)
    at lookupAndConnectMultiple (node:net:1401:3)
    at node:net:1347:7
    at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)
    at lookupAndConnect (node:net:1346:5)
    at Socket.connect (node:net:1243:5)
    at Agent.connect [as createConnection] (node:net:233:17)
    at Agent.createSocket (node:_http_agent:342:26)
    at Agent.addRequest (node:_http_agent:289:10)
    at new ClientRequest (node:_http_client:337:16)
    at request (node:http:101:10)
    at console.warn.workerData.workerData (/path/to/reproduce.ts:71:29)
    at new Promise (<anonymous>)
    at <anonymous> (/path/to/reproduce.ts:70:11)
    at ModuleJob.run (node:internal/modules/esm/module_job:192:25)
    at async CustomizedModuleLoader.import (node:internal/modules/esm/loader:228:24)
    at async loadESM (node:internal/process/esm_loader:40:7)
    at async handleMainPromise (node:internal/modules/run_main:66:12)
Emitted 'error' event on Socket instance at:
    at emitErrorNT (node:internal/streams/destroy:151:8)
    at emitErrorCloseNT (node:internal/streams/destroy:116:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  errno: -22,
  code: 'EINVAL',
  syscall: 'connect',
  address: '{{TEST_CONTAINER_IP}}',
  port: 80
}

Node.js v20.4.0

@loynoir
Copy link
Author

loynoir commented Jul 14, 2023

I suggest nodejs wrap http.requestOptions.lookup with process.nextTick.

If so, all six test cases should be catch-able.

If not,

  • in some condition, not using worker, http.request may throw not-catch-able error.

  • in some condition, using worker, http.request throw catch-able error, exitCode not zero even error is caught.

I use http.request with lookup and did encounter http.request throw not-catch-able error, which I cannot reproduce.

@loynoir
Copy link
Author

loynoir commented Jul 14, 2023

@bnoordhuis

This here is likely your problem. It gets kind of obfuscated due to the stitched-together async stack trace but that's a synchronous "bad parameter" exception, not a runtime network error like the others.

Oh, I know what you mean, I replace them, not node. I think better for reading.

@loynoir
Copy link
Author

loynoir commented Jul 14, 2023

Close in flavor of smaller reproduce.

#48771

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants