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

Unhandled 'error' event. No such file error at at SFTPStream._transform (ssh2-streams\lib\sftp.js) #6

Open
zacronos opened this issue Apr 28, 2017 · 8 comments
Labels

Comments

@zacronos
Copy link
Member

Originally from: realtymaps/promise-ftp#19

This exception is actually from promise-sftp library. However promise-sftp is not letting users create an issue.

Here is a code snippet to reproduce the error:

var PromiseSftp = require('promise-sftp');

function test() {
  const ftp = new PromiseSftp();

  return ftp.connect({
    host: 'host.address',
    user: 'username',
    password: 'password',
    port: 22,
  })
    .then(() => {
      console.log('connected');

      return ftp.put(Buffer.from('this is a test'), '/invalid/path/to/file.txt')
        .then(() => {
          console.log('successfully uploaded');
        })
        .catch(err => {
          console.log('failed: ' + JSON.stringify(err));
        });
    })
    .then(() => ftp.end())
    .catch(err => {
      console.log('ftp end error: ' + JSON.stringify(err));
    });
}

Since the path does not exist on the ftp server, the promise should be rejected. It is resolving successfully and throwing an error when we attempt to end the connection.

Output:

  connected
  successfully uploaded
  events.js:160
      throw er; // Unhandled 'error' event

Error: No such file
    at SFTPStream._transform (~\node_modules\ssh2-streams\lib\sftp.js:405:27)
    at SFTPStream.Transform._read (_stream_transform.js:167:10)
    at SFTPStream._read (~\node_modules\ssh2-streams\lib\sftp.js:181:15)
    at SFTPStream.Transform._write (_stream_transform.js:155:12)
    at doWrite (_stream_writable.js:334:12)
    at writeOrBuffer (_stream_writable.js:320:5)
    at SFTPStream.Writable.write (_stream_writable.js:247:11)
    at Channel.ondata (_stream_readable.js:555:20)
    at emitOne (events.js:96:13)
    at Channel.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:176:18)
    at Channel.Readable.push (_stream_readable.js:134:10)
    at SSH2Stream.<anonymous> (~\node_modules\ssh2\lib\Channel.js:166:15)
    at emitOne (events.js:96:13)
    at SSH2Stream.emit (events.js:188:7)
    at parsePacket (~\node_modules\ssh2-streams\lib\ssh.js:3439:10)
    at SSH2Stream._transform (~\node_modules\ssh2-streams\lib\ssh.js:667:13)
    at SSH2Stream.Transform._read (_stream_transform.js:167:10)
    at SSH2Stream._read (~\node_modules\ssh2-streams\lib\ssh.js:251:15)
    at SSH2Stream.Transform._write (_stream_transform.js:155:12)
    at doWrite (_stream_writable.js:334:12)
    at writeOrBuffer (_stream_writable.js:320:5)
@zacronos
Copy link
Member Author

@ozankaya

@zacronos
Copy link
Member Author

The exception originates in the underlying ssh2-streams library. The fact that it is coming through in the wrong place is a consequence of an unfortunate decision I made regarding how to handle waiting for commands to complete under the hood -- that part needs to be redesigned. Thanks for the bug report.

@zacronos zacronos added the bug label Apr 28, 2017
@lfreneda
Copy link

@zacronos any workaround?

@zacronos
Copy link
Member Author

@lfreneda I would suggest using .list() to verify a file / directory's presence. And if the file/directory isn't there, do whatever makes sense (aborting for a file download, creating the directory for a file upload, etc).

It's a bit inconvenient because you'd need to do it for each path segment, but it should work.

@at-teamexports
Copy link

Hello

We encountered this error.
We have developed a fix.
In the "put" function, instead of:

      this.put = function(input, destPath) {
        return Promise["try"](function() {
          var options;
          if (restartOffset !== null) {
            options = {
              start: restartOffset,
              flags: 'r+'
            };
            restartOffset = null;
          }
          if (typeof input === 'string') {
            if (!options) {
              return promisifiedClientMethods.fastPut(input, destPath);
            }
            input = fs.createReadStream(input);
          }
          return promisifiedClientMethods.createWriteStream(destPath, options).then(function(stream) {
            finishLogic(stream);
            if (input instanceof Buffer) {
              return stream.end(input);
            }
            input.pipe(stream);
            return void 0;
          });

Just have to code:

      this.put = function(input, destPath) {
        return Promise["try"](function() {
          var options;
          if (restartOffset !== null) {
            options = {
              start: restartOffset,
              flags: 'r+'
            };
            restartOffset = null;
          }
          if (typeof input === 'string') {
            if (!options) {
              return promisifiedClientMethods.fastPut(input, destPath);
            }
            input = fs.createReadStream(input);
          }
          return promisifiedClientMethods.createWriteStream(destPath, options).then(function(stream) {
            return new Promise((resolve, reject) => {
              stream.on('error', (err) => {
                console.log('write stream error ' + err);
                reject(err);
              });
              stream.on('end', () => {
                resolve();
              });
              finishLogic(stream);
              if (input instanceof Buffer) {
                stream.end(input);
                resolve();
                return;
              }
              input.pipe(stream);
            });
          });

The problem was that the "put" function does not handle the errors of the write stream. I don't know well about bluebird promises but there was no stream.on('error', ...). That is why, when the folder does not exist, the thread was killed.
With this solution, "put" function rejects the write stream errors and resolve when the write stream ends.

About your "list directory" workaround. This is not a solution for us since some FTP servers allow put actions but not list directories.

Would it be possible to integrate this fix please? I am not familiar to coffee script...

@at-teamexports
Copy link

A little update, here is the code we actually use:

      this.put = function(input, destPath) {
        return new Promise((resolve, reject) => {
          var options;
          if (restartOffset !== null) {
            options = {
              start: restartOffset,
              flags: 'r+'
            };
            restartOffset = null;
          }
          if (typeof input === 'string') {
            if (!options) {
              return promisifiedClientMethods.fastPut(input, destPath);
            }
            input = fs.createReadStream(input);
          }
          return promisifiedClientMethods.createWriteStream(destPath, options).then(function(stream) {
            finishLogic(stream);
            stream.on('error', (err) => {
              reject(err);
            });
            input.on('end', () => {
              resolve();
            });
            if (input instanceof Buffer) {
              stream.end(input);
              resolve();
              return;
            }
            input.pipe(stream);
          });
        });
      };

@em
Copy link

em commented Dec 6, 2018

Please make this error message include the actual filename in it.

@juanmjacobs
Copy link

This fix would be awesome! Currently I have no way to catch those underlying stream errors and they make my process crash.
Thanks for this great library!

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

No branches or pull requests

5 participants