node-pty icon indicating copy to clipboard operation
node-pty copied to clipboard

EIO when process ends

Open vbarbarosh opened this issue 6 years ago • 11 comments

How to reproduce:

$ node --version
v8.5.0
$ cat package.json | grep node-pty
"node-pty": "^0.7.4",
$ node -e "
const node_pty = require('node-pty');
const pty = node_pty.spawn('bash');
pty.on('error', error => console.error(error));
pty.write('exit\r\n');
"
{ Error: read EIO
    at _errnoException (util.js:1026:11)
    at Pipe.onread (net.js:606:25) code: 'EIO', errno: 'EIO', syscall: 'read' }

vbarbarosh avatar Mar 14 '18 20:03 vbarbarosh

It seems that something wrong with my system (I can't reproduce it using docker run --rm -it node:8.5.0 bash).

vbarbarosh avatar Mar 14 '18 20:03 vbarbarosh

It happens on

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 9.4 (stretch)
Release:	9.4
Codename:	stretch
$ node --version
v9.8.0

and on

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.3 LTS
Release:	16.04
Codename:	xenial
$ node --version
v8.4.0

and on

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 14.04.5 LTS
Release:	14.04
Codename:	trusty
$ node --version
v6.2.1

vbarbarosh avatar Mar 14 '18 21:03 vbarbarosh

Are you on Windows running through WSL?

Tyriar avatar Mar 15 '18 14:03 Tyriar

No, I use Debian on my laptop and Ubuntun on VPS.

vbarbarosh avatar Mar 15 '18 16:03 vbarbarosh

Perhaps some race condition is happening? Does the same occur if you wrap the exit write in a setTimeout(..., 1000)?

Tyriar avatar Mar 15 '18 17:03 Tyriar

Yes, the following also gets error:

const node_pty = require('node-pty');
const pty = node_pty.spawn('bash');
pty.on('error', error => console.error(error));
setTimeout(function () {
    pty.write('exit\r\n');
}, 1000);
"
{ Error: read EIO
    at Pipe.onread (net.js:599:25) errno: 'EIO', code: 'EIO', syscall: 'read' }

vbarbarosh avatar Mar 15 '18 18:03 vbarbarosh

Well EIO is a generic I/O error, I would guess it's related to your environment because I've never seen this. Not sure how much more help I can be on this.

Tyriar avatar Mar 22 '18 19:03 Tyriar

I've had at least one user report the issue above and I can also reproduce locally. It happens as part of normal program execution and afterwards exit callback is invoked with (0, 0).

ioquatix avatar May 05 '18 07:05 ioquatix

i can reproduce this when the command i run doesn't produce stdout. for example:

pty.spawn('sh', ['-c', 'sleep 1'])
// or
pty.spawn('sh', ['-c', 'echo hi && sleep 1'])

this produces two data events, one with hi and another that's empty.

anderoonies avatar May 24 '18 22:05 anderoonies

Same error on my server CenOS 8.0.

const nodePty = require('node-pty');

const pty = nodePty.spawn('login', []);
pty.on('data', function(data){
  process.stdout.write(data);
});

pty.on('error', function(err){
  console.error('error', err);
});

WTF: After adding blocking, there will be no error

const nodePty = require('node-pty');

const pty = nodePty.spawn('login', []);
const now = Date.now();
while(Date.now() < (now + 50)){ // blocking
}
pty.on('data', function(data){
  process.stdout.write(data);
});

pty.on('error', function(err){
  console.error('error', err);
});

hezedu avatar Feb 24 '21 13:02 hezedu

This is because pty'fd is in non-blocking mode. It happened at the end, not bad, the worst happened at the beginning. Just like above.
My solution is: Block in the beginning and ignore the end error. I improved the blocking code. See below:

// Fixed with my cheap server CenOS 8.0.
const fs = require('fs');
const nodePty = require('node-pty');

const encoding = 'utf-8';
const pty = nodePty.spawn('login', [], { encoding });

pty.on('data', function(data){
  process.stdout.write(data);
});

let isClose = false;
pty.on('close', function(){
  isClose = true;
  console.log('close');
});
pty.on('error', function(err){
  setTimeout(() => {
    if(isClose && (error.code === "EIO" ||
    error.code === "EAGAIN" || 
    error.code === "EBADF" || 
    error.code === "EWOULDBLOCK")){ // Ignore the end error.
      return;
    }
    console.error('error', err);
  });
});

const firstBuffer = blockingWhenUnReadable(pty._fd); // Block in the beginning
pty.emit('data', firstBuffer.toString(encoding));

function blockingWhenUnReadable(fd){
  const buffer = Buffer.alloc(1);
  let isContinue = true;
  while(isContinue){
    try {
      fs.readSync(fd, buffer);
      isContinue = false;
    } catch(e){
      // console.error('err.code', e.code);
      if(e.code === 'EAGAIN' || 
      e.code === "EIO" || 
      e.code === "EWOULDBLOCK"){
        isContinue = true;
      } else {
        throw e;
      }
    }
  }
  return buffer;
}

hezedu avatar Mar 30 '21 02:03 hezedu