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

process.send is not a function in child spawned with babel-node (ipc doesn't work) #4554

Closed
jedwards1211 opened this issue Sep 23, 2016 · 23 comments · Fixed by #12836 or #13037
Closed
Labels
help wanted outdated A closed issue/PR that is archived due to age. Recommended to make a new issue pkg: node

Comments

@jedwards1211
Copy link
Contributor

Input Code

spawn.js:

import {spawn} from 'child_process'

spawn('babel-node', ['child.js'], {
  stdio: [0, 1, 2, 'ipc']
})

child.js:

process.send({hello: 'world'})

Babel Configuration (.bablerc, package.json, cli command)

{
  "presets": ["es2015", "stage-1"]
}

Expected Behavior

No errors are thrown when I spawn child.js with node.

Current Behavior

When I spawn child.js with babel-node, I see the following error in the console:

child.js:3
process.send({ hello: 'world' });
        ^

TypeError: process.send is not a function
    at Object.<anonymous> (/Users/andy/smart-restart/child.js:1:9)
    at Module._compile (module.js:413:34)
    at loader (/Users/andy/.nvm/versions/node/v5.12.0/lib/node_modules/babel-cli/node_modules/babel-register/lib/node.js:146:5)
    at Object.require.extensions.(anonymous function) [as .js] (/Users/andy/.nvm/versions/node/v5.12.0/lib/node_modules/babel-cli/node_modules/babel-register/lib/node.js:156:7)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Function.Module.runMain (module.js:447:10)
    at /Users/andy/.nvm/versions/node/v5.12.0/lib/node_modules/babel-cli/lib/_babel-node.js:151:24
    at Object.<anonymous> (/Users/andy/.nvm/versions/node/v5.12.0/lib/node_modules/babel-cli/lib/_babel-node.js:152:7)
    at Module._compile (module.js:413:34)

Context

I'm the author of smart-restart. I'm trying to get it to work when spawning the supervised process with babel-node.

Your Environment

software version
Babel 6.14.0
node 5.12.0
npm 3.8.6
Operating System OS X 10.11.6
@bundacia
Copy link

@jedwards1211 have you found any workaround for this issue?

@jedwards1211
Copy link
Contributor Author

@bundacia No, I haven't figured out any way to get child process IPC to work with babel-node. It's always possible to communicate with UNIX Sockets, HTTP, WebSockets, etc. though.

@bundacia
Copy link

bundacia commented Oct 2, 2016

ok, I'll keep trying. For some reason I can't get plain old sockets working in node and most of the examples just use this process.send wrapper.

@bundacia
Copy link

bundacia commented Oct 3, 2016

As a little more context, it seems that I can't open extra file descriptors at all using spawn when spawning a babel-node process. Consider this example:

// parent.js
var child_process = require('child_process')

var child = child_process.spawn("./node_modules/.bin/babel-node", ['./child.js'], {
  stdio: [0, 1, 2, 'pipe'],
})

child.stdio[3].on('data', m => console.log('KID SAY (fd: 3):', m.toString()))
// child.js
var fs = require('fs')
var fd3 = fs.createWriteStream(null, {fd: 3})

fd3.write('hello')

OUTPUT:

events.js:154
      throw er; // Unhandled 'error' event
      ^

Error: EBADF: bad file descriptor, write
    at Error (native)

If I change spawn("./node_modules/.bin/babel-node", ...) to just spawn("node", ...) I get this:

KID SAY (fd: 3): hello

@bundacia
Copy link

bundacia commented Oct 3, 2016

I think I may have found the problem. the babel-node script itself calls spawn like this:

https://github.com/babel/babel/blob/master/packages/babel-cli/src/babel-node.js#L84:

let proc = child_process.spawn(process.argv[0], args, { stdio: "inherit" });

Using the string "inherit" as the stdio option causes the new process to inherit stdin, stdout, and stderr, but nothing else (see https://github.com/nodejs/node/blob/master/lib/internal/child_process.js#L759). So that's where the extra file descriptors get lost. I think using require("babel-register") in the child instead of invoking the babel-node executable could be a viable workaround for this.

@jedwards1211
Copy link
Contributor Author

jedwards1211 commented Oct 3, 2016

@bundacia interesting, but it only uses spawn as a fallback if kexec doesn't work, and I'm on OS X, so I'd think kexec should work...I'll have to try a simple repro with just kexec at some point and if that fails, file an issue there

@jedwards1211
Copy link
Contributor Author

jedwards1211 commented Oct 3, 2016

@kittens would there be major downsides to just meddling with the process' own argv and then running _babel_node directly instead of using kexec or spawn?

@hzoo
Copy link
Member

hzoo commented Mar 19, 2017

Closing for inactivity, if this is still an issue we can reopened or maybe check a pr?

@hzoo hzoo closed this as completed Mar 19, 2017
@reconbot
Copy link

reconbot commented Apr 2, 2017

This is still an issue and kexec doesn't fix it as it only deals with the default 3 file descriptors.

@reconbot
Copy link

reconbot commented Apr 2, 2017

I tested the following to no success.

let proc = child_process.spawn(process.argv[0], args, { 
  stdio: ["inherit", "inherit", "inherit", "inherit"]
});

After reading through the stdio and child_process code a bit, I have two theories.

  • While we can give the fd for the ipc channel to a new spawned process the ipc is already in progress in the babel-node's node process so can't be "forwarded" as a "session" is already in progress.
  • The fd for an ipc channel is somehow marked as ipc. This first marked fd gets set as process.channel and used for ipc. So even if we inherit the 4th fd, it's not marked as ipc in the new processes and therefore not used by the spawned process. Maybe we can mark it somehow

I'd bet it's something like the 2nd theory, I'm going to cc @cjihrig and @bnoordhuis who are the child_process working group to see if they can clarify. It would really awesome for my users if they could seamlessly use babel-node in development of child_process using apps.

@hzoo hzoo reopened this Apr 2, 2017
@cjihrig
Copy link

cjihrig commented Apr 2, 2017

Ben would probably know the most official way to do this, if one exists. Passing the IPC channel and the file descriptor both failed for me, but I was able to proxy the IPC channel like this.

@reconbot
Copy link

reconbot commented Apr 2, 2017 via email

@bnoordhuis
Copy link

You have an existing ipc file descriptor that you want to recognize the child process as such? Spawn it with { stdio: ['inherit', 'inherit', 'inherit', fd], env: { NODE_CHANNEL_FD: 3 } }. (Undocumented but present since basically forever.)

Note that you can't use the file descriptor in two processes at the same time, though.

@hbarcelos
Copy link

Any progress here?

@calidion
Copy link

calidion commented May 6, 2018

will this be fixed?

@ThibaultJanBeyer
Copy link

+1 please fix or provide an alternate solution

@dyegros
Copy link

dyegros commented Oct 8, 2018

waiting for a fix or workaround too

@nicolo-ribaudo
Copy link
Member

I'm not very familiar with node processes (and I don't have access to a PC), but if anyone wants to investigate this issue I'd happily review a PR. If you need help, you can join our slack channel 🙂

@emahuni
Copy link

emahuni commented Mar 1, 2020

@jedwards1211
Copy link
Contributor Author

@emahuni that misses the point, process.send is 100% supposed to be available when the process was spawned with an ipc channel, but this isn't working when the process was spawned from babel-node. process.send = process.send || () => {} isn't a workaround because we need to communicate with the parent process, not just prevent process.send() from throwing an error.

@lambertkevin
Copy link
Contributor

Ben would probably know the most official way to do this, if one exists. Passing the IPC channel and the file descriptor both failed for me, but I was able to proxy the IPC channel like this.

Also facing this issue, and this solution has been removed from github. Any chance to get this solution back ?

@jedwards1211
Copy link
Contributor Author

jedwards1211 commented Feb 19, 2021

I don't think this has been mentioned, one workaround is to spawn node on a module that does require('@babel/register'); require('./yourModule') instead of using babel-node. (If you use require.main anywhere you would need to adapt it for this workaround)

@lambertkevin
Copy link
Contributor

lambertkevin commented Mar 21, 2021

This issue has been closed but the fix doesn't seem to work if the child is spawn with babel-node.

parent.js

const { spawn } = require('child_process');

const child = spawn('babel-node', ['child.js'], {
  stdio: ['inherit', 'inherit', 'inherit', 'ipc']
});

child.on('message', (msg) => {
  console.log('received from child', msg);
});

child.js

process.send('hello');

babel-node parent.js will not log anything.

Changing spawn('babel-node', ... to spawn('node', ... will log received from child test.

EDIT: Tested with 7.13.10

EDIT 2: Should now be working with ^7.13.13 ! 👍

@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Jun 29, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 29, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
help wanted outdated A closed issue/PR that is archived due to age. Recommended to make a new issue pkg: node
Projects
None yet