question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

100% reproducible bug that results in node process crash when using promises (bug in promise reject)

See original GitHub issue

Version: 9.0.4 Node: v18.12.0 OS: Mac OS 12.1

Problem manifests itself if network becomes inaccessible after Client.connect was already successfully executed but before Client.put method is called.

Code Snippet:

let Client = require('ssh2-sftp-client');
let sftp = new Client();

function sleep(ms) {
  return new Promise((r) => setTimeout(r, ms));
}

async function run() {
  const startTime = Date.now();
  const filename = `random-file-name-${startTime}`;

  const fileData = Buffer.from('TRACER FILE');
  const remotePath = `incoming/${filename}`;

  try {
    console.log(`---- Before connect ----- `);

    await sftp.connect({
      host: '1.1.1.1',
      port: 22,
      username: 'user',
      password: 'password',
    });

    console.log(`---- After connect ----- `);
    
    console.log(`---- Sleep for 20s ----- `);

    // !!!!!!!   BREAK CONNECTION HERE while sleeping
    await sleep(20000);

    console.log(`---- Before put ----- `);
    const result = await sftp.put(fileData, remotePath);
    console.log(`---- After put ----- `);
    console.log(`SFTP put result: ${result}`);
    sftp.end();
  } catch (err) {
    console.error(`Error: ${err.message}`);
  }

  await sleep(7000);

  console.log('The End');
}

run();

Log from the run that completes successfully:

---- Before connect ----- 
---- After connect ----- 
---- Sleep for 20s ----- 
---- Before put ----- 
---- After put ----- 
SFTP put result: Uploaded data stream to incoming/random-file-name-1670562812521
The End

Log from the run that crashes with disconnect during sleep(20000):

---- After connect ----- 
---- Sleep for 20s ----- 
---- Before put ----- 
/project/node_modules/ssh2-sftp-client/src/utils.js:25
      throw newError;
      ^

Error: put: read ETIMEDOUT
    at Client.fn (/project/node_modules/ssh2-sftp-client/src/utils.js:20:22)
    at Client.emit (node:events:525:35)
    at Socket.<anonymous> (/project/node_modules/ssh2/lib/client.js:745:12)
    at Socket.emit (node:events:513:28)
    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) {
  code: 'ETIMEDOUT'
}

Node.js v18.12.0

It looks that for some reason in this use-case errorListener method in utils.js is invoked with reject parameter set to undefined resulting in exception vs promise reject.

How to break connection for MacOS:

# PF should be started: sudo pfctl -E
# Check current ruleset: sudo pfctl -sr
# To Apply this ruleset file: sudo pfctl -f ./pf-block-ip.conf
# To Flush (all) rulesets: sudo pfctl -F rules

# Block incoming traffic from some IP
block in quick from 1.1.1.1

Issue Analytics

  • State:open
  • Created 9 months ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
theophilusxcommented, Dec 12, 2022

Tim,

I’d like to emphasize the fact, that this crash is 100% reproducible and not dependent on any variations. You just need to run the snippet I provided, drop connection during the sleep and you’ll reproduce the problem, I’m absolutely sure about this. I strongly recommend to run the snippet at least once - you’ll instantly see the issue.

I"m not questioning the fact it crashes. However, I’m not sure your conclusion is correct. As I stated before, I won’t have time to look at this in any detail until after the new year.

If you can diagnose further or if you want to provide a PR, that would be great, otherwise, you will have to wait until I get to it.

0reactions
chubbobcommented, Dec 12, 2022

Sounds good.

The reason why exception is not bubbling up to my try/catch is identical to the issue with the code below. This code will result in unhandled exception as well:

async function run2() {
  try {
    setTimeout(() => {
      throw new Error('this is a test message');
    }, 10);
  } catch (err) {
    console.log('ERROR');
  }
}

try {
  run2()
    .then(() => {
      console.log('Run executed SUCCESSFULLY');
    })
    .catch((err) => {
      console.log('Run FAILED:', err);
    });
} catch (err) {
  console.error('ERROR: external catch');
  console.error(`Error: ${err.message}`);
}

Here is the resulting log:

Run executed SUCCESSFULLY
/project/sftp-bug-repro.js:63
      throw new Error('this is a test message');
      ^

Error: this is a test message
    at Timeout._onTimeout (/project/sftp-bug-repro.js:63:13)
    at listOnTimeout (node:internal/timers:564:17)
    at process.processTimers (node:internal/timers:507:7)

Node.js v18.12.0
Read more comments on GitHub >

github_iconTop Results From Across the Web

Promise.reject() crashes repl when using --unhandled ...
What steps will reproduce the bug? Launch the repl with this command: node --unhandled-rejections=strict.
Read more >
ssh2-sftp-client - Bountysource
ssh2-sftp-client · No issues · 100% reproducible bug that results in node process crash when using promises (bug in promise reject) $ 0....
Read more >
Node throws unhandled promise rejection while using ...
I discovered a weird issue while trying to implement a function that awaits all promises of an array and catches all errors.
Read more >
Node.js 15 Is Out! What Does It Mean for You? - Maxim Orlov
The recommended approach is to let the application crash since it might be in a faulty state that might lead to more errors...
Read more >
ssh2-sftp-client - npm
Start using ssh2-sftp-client in your project by running `npm i ... Fix bug in end() method where it was possible for the module...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found