lpappone lpappone - 4 months ago 17
Node.js Question

node.js child process doesn't work in node webkit

I made a small app with node webkit. Pre-packaging, it works fine. But after I zipped it and added it to Contents/Resources in node-webkit.app I get an error when I run the app. It opens fine, but the task it does involves child processes, and I get this error:

Uncaught node.js Error
Error: spawn ENOENT.

I'm guessing it might be something related to the issue raised in this question: Node-Webkit Child Process Exec

because my child processes are calling pdftk, a separate command line program. Ultimately, I'd love to install pdftk as part of my app - I have not been able to figure out how to do this. I tried including it as one of the things to be zipped with the rest of the app, but that caused the app to crash immediately after launch (it would open a window with the correct title but no contents, which would immediately close).

So, main question is, how do I install pdftk as part of a packaged node-webkit app, so that the app can be launched simply by double clicking the icon rather than using the command line? Thanks for any help.


I am assuming your code in question is executed via the node-main entry point of node-webkit: https://github.com/rogerwang/node-webkit/wiki/Node-main

If any exception happens (there) which is not catched in your application will crash.

Sadly at the moment the breakpad feature for getting crashdumps is not working on OSX: https://github.com/rogerwang/node-webkit/issues/2518

How to prevent Node-Webkit from crashing immediately

Wrap the code in try/catches to prevent the crash and get information why the crash occurs.

try {

    the_child_process = child_process.spawn(pathToBin, args);

} catch (err) {

    global.console.log( "Error while trying to start child process: " + JSON.stringify(err) );

This is a general advice for a situation like you are experiencing to track down the real cause for the issue.

How to include a binary with your node-webkit app

There are a few things involved.

  1. Including the binaries inside your app.nw

    This should be self explanatory - but there is one caveat which caused me some trouble: Make sure the binary is marked as executable via chmod 755. If you are using grunt you might like grunt-chmod. Now your binaries are part of your app's package and you can execute them by knowing the absolute path.

  2. Resolve the path to the binary at runtime even when packaged. The following piece of code is my solution for selecting the right binary for the current platform assuming your tool is multi platform. Also it assumes your binaries are ordered in a certain folder structure. Alternatively select the right binary in your build process and use always the same path.

    var arch = process.arch;
    var platform = process.platform;
    // this will return the root path of your app-package at runtime
    var rootDir = process.cwd(); 
    var isWin = false;
    var execPath = rootDir;
    // some base path is appended
    execPath = path.join(execPath, 'path', 'to', 'bin');
    // select folder for current platform
    switch (platform) {
        case 'darwin':
            execPath = path.join(execPath, 'mac');
        case 'linux':
            execPath = path.join(execPath, 'lin');
        case 'win32':
            execPath = path.join(execPath, 'win');
            isWin = true;
            global.console.log("unsupported platform: " + platform);
            return null;
    // select folder for current processor architecture
    switch (arch) {
        case 'ia32':
            execPath = path.join(execPath, 'x86');
        case 'x64':
            execPath = path.join(execPath, 'x64');
            global.console.log("unsupported architecture: " + arch);
            return null;
    // add executable filename
    execPath = path.join(execPath, 'node');
    if (isWin) {
        execPath = execPath + ".exe";
    global.console.log("Path to your binary: " + execPath);
    return execPath;
  3. Resolve the paths which are fed to your binary as arguments eventually. This was also a bit confusing because all paths were treated as relative to the app's package root path. My node-main file resides in a folder in my app package so I thought I should reference files relative from there, but this was not the case.

    app package root
    |--- package.json     <- node-webkit package.json
    |--- client           <- here my sources for the frontend reside
    |--- server           
    |----|--- node_modules  <- server runtime dependencies
    |----|--- src           <- server source code
    |----|----|--- server.js  <- this is my node server file to execute via node
    |--- node-webkit      <- node webkit code and dependencies
    |----|--- bin           <- a directory with my deployed binaries
    |----|--- node-main.js  <- this is my node main file

To invoke a node binary with my server file the following line led to success:

    child_process.spawn(absPathToNodeBin, "server/src/server.js");