Node.js catch ENOMEM error thrown after spawn - javascript

My Node.js script crashes because of a thrown ENOMEM (Out of memory) errnoException when using spawn.
The error:
child_process.js:935
throw errnoException(process._errno, 'spawn');
^
Error: spawn ENOMEM
at errnoException (child_process.js:988:11)
at ChildProcess.spawn (child_process.js:935:11)
at Object.exports.spawn (child_process.js:723:9)
at module.exports ([...]/node_modules/zbarimg/index.js:19:23)
I'm already using listeners for the error and exit event, but non of them getting fired in case of this error.
My code:
zbarimg = process.spawn('zbarimg', [photo, '-q']);
zbarimg.on('error', function(err) { ... });
zbarimg.on('close', function(code) { ... });
Full source code available.
Is there anything I can do to prevent the script from crashing? How do I catch the thrown ENOMEM error?

I had the same problem and as it turned out, my system had no swap space enabled. Check if this is the case by running the command free -m:
vagrant#vagrant-ubuntu-trusty-64:~$ free -m
total used free shared buffers cached
Mem: 2002 233 1769 0 24 91
-/+ buffers/cache: 116 1885
Swap: 0 0 0
Looking at the bottom row we can see we have a total of 0 bytes swap memory. Not good. Node can get pretty memory hungry and if no swap space is available when memory runs out, errors are bound to happen.
The method for adding a swap file varies between operating systems and distributions, but if you're running Ubuntu like me you can follow these instructions on adding a swap file:
sudo fallocate -l 4G /swapfile Create a 4 gigabyte swapfile
sudo chmod 600 /swapfile Secure the swapfile by restricting access to root
sudo mkswap /swapfile Mark the file as a swap space
sudo swapon /swapfile Enable the swap
echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab Persist swapfile over reboots (thanks for the tip, bman!)

If you ever run into this problem in AWS Lambda, you should consider increasing the memory allocated to the function.

You can try changing the amount of memory node uses with this command:
node ----max-old-space-size=1024 yourscript.js
--max-old-space-size=1024 will allocate 1 gig of memory.
By default node will use 512 mb of ram but depending on your platform you may need to allocate more or less so the garbage collection kicks in when you need it.
If your platform has less than 500 mb of ram available then try setting the memory usage lower to --max-old-space-size=256.

This solved my problem :)
The issue with memory
free -m
fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo “/swapfile none swap sw 0 0” | sudo tee -a /etc/fstab

I've had the same problem and fixed with try / catch:
try {
zbarimg = process.spawn('zbarimg', [photo, '-q']);
} catch (err) {
console.log(err);
}
zbarimg.on('error', function(err) { ... });
zbarimg.on('close', function(code) { ... });

I fixed the issue by just disabling and re-enabling my Node Server.

Related

"istanbul ignore next" command seems to be ignored

I'm trying to use Jest to test my code. It was working quite fine until I tried to exclude a class method from the tests.
The querySelector() call is the reason why I want to skip this method from being tested ("document" is obviously null unless I run the script in the browser)
I tried this solution, which seems to be the most suggested one:
/* istanbul ignore next */
appendNewInputFields() {
const howMany = Number(document.querySelector('#items-to-add').value);
[...Array(howMany)].forEach( i => {
const newInputField = this.createNewItemInputField();
this.inputItemsContainerNode.append(newInputField);
});
}
But the test keeps failing and the line /* istanbul ignore next */ seems to be ignored.
I've also tried putting the comment between the function signature and its body (as was suggested somewhere here on SO), but no luck:
FAIL js/DOMManager.test.js
● Test suite failed to run
TypeError: Cannot read property 'value' of null
135 | appendNewInputFields() /* istanbul ignore next */ {
136 |
> 137 | const howMany = Number(document.querySelector('#items-to-add').value);
| ^
138 |
I've read around that this might be related to babel-plugin-istanbul. I've tried
npm --save-dev uninstall babel-plugin-istanbul
which in the terminal returned:
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents#2.3.2 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents#2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
removed 18 packages and audited 521 packages in 2.178s
but the folder babel-plugin-istanbul is still present in /node_modules/ inside my working folder and the test keeps failing as if nothing changed.
If I comment out the body of the function, the other test suites work perfectly. If I try to apply the ignore next command to any other part of the code, the tests pass just fine and the line is completely ignored.
If I try to manually delete the /babel-plugin-istanbul/ folder (from the /node_modules/ in my working folder), Jest stops working.
(This is the first time I installed Node.js, and I did it only because I wanted to start unit testing with Jest. I'm pointing this out because these are my first steps venturing out of the vanilla world. I don't know how to deal with Node.js nor npm, I just launched a couple commands to install it, I wrote a few tests for Jest and they all immediately worked fine. I'm not using any other framework, I'm trying to stick to vanilla JS as much as possible.)
----------------------- Edit:
I tried changing the code to this:
appendNewInputFields() {
// TODO solve the istanbul ignore issue
let howMany;
/* istanbul ignore if */
if(document != null) {
howMany = Number(document.querySelector('#items-to-add').value);
[...Array(howMany)].forEach( i => {
const newInputField = this.createNewItemInputField();
this.inputItemsContainerNode.append(newInputField);
});
console.log("added " + howMany + " input fields");
}
}
I keep getting
FAIL js/DOMManager.test.js
● Test suite failed to run
TypeError: Cannot read property 'value' of null
139 | /* istanbul ignore if */
140 | if(document != null) {
> 141 | howMany = Number(document.querySelector('#items-to-add').value);
| ^
142 |
143 | [...Array(howMany)].forEach( i => {
144 | const newInputField = this.createNewItemInputField();
I'm totally clueless at this point. Given the conditional, "document" should be null and that block entirely skipped, but it keeps failing the test.
The same code works exactly as intended when run in the browser.
So I made a few tests in a blank new folder, and it finally struck me.
Apparently I had misunderstood the purpose of /* istanbul ignore next */.
Its function is not to skip code from being executed during the tests, but rather prevent that portion of code to be taken into account when determining the amount of total code that has been tested. The code runs (if there is anything calling that function), but those lines just don't count when Jest sums up the amount of lines it tested (which is the purpose of --coverage, I guess). If an error occurs, it is thrown as it would normally be.
My problem wasn't really related to Jest nor the istanbul package. I feel pretty dumb realizing this now, but what I needed was just proper exception handling.
I implemented a few old fashioned try/catch blocks: now everything is tested smoothly and the istanbul ignore directive correctly behaves as expected (now that I know what to expect, that is): functions that are not tested and flagged to be ignored, they just don't appear in the final coverage report.
I hope this helps anybody who might stumble in my same misunderstanding.
Of course, if anybody more competent than me can confirm this interpretation or has any suggestion or further explanation, it would obviously be much appreciated.
I was with the same error with window.alert command with jest:
ReferenceError: alert is not defined
4 |
5 | /* istanbul ignore next */
> 6 | alert(helloWorld());
| ^
7 |
so I tried to use try-catch and SUCCESS!
before:
alert(helloWorld());
after:
const alerta = () => {
try {
return alert(helloWorld());
} catch (error) {
return false;
}
}
testes:
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
This might also happen if your bundling tool (esbuild, for instance) removes comments, therefore istanbul does not see them.
If that's the case, you might want to change your bundling tool to something like terser (make sure that comments are not omitted there, too) or adjust minification options of the bundling tool you use.
This is how it would look like if you use Vite:
build: {
minify: 'terser',
terserOptions: {
format: {
comments: 'all', // or regular expression /istanbul\signore\s/
},
},
},

How to run all test scripts on a single browser instance

I am using Testcafe (free version) with Java Script. I want to run all my test cases (resides in multiple test scripts in __test__ directory) in a single browser instance (That way 1 time log in) per browser type.
For example, 1 instance for chrome and 1 instance for safari but all tests will run before closing the browser.
If a test fails, I want the screenshot is taken and count number of the test failures. But do want to continue.
I'm doing all on Node 12 Docker image, so it is best if I don't need to install anything else.
How do I do this with Testcafe?
const createTestCafe = require('testcafe')
let testcafe = null
let runner = null
createTestCafe('localhost', 1337, 1338)
.then(tc => {
testcafe = tc
const runner = testcafe.createRunner()
return runner
.src([ '__test__/*.js' ])
.browsers([ 'chrome:headless --no-sandbox --disable-gpu', 'safari' ])
.screenshots('./reports/screenshots/', true)
.run({
selectorTimeout: 10000,
assertionTimeout: 10000,
})
})
runner
.screenshots({
path: 'reports/screenshots/',
takeOnFails: true,
})
.then(failedCount => {
console.log('Tests failed: ' + failedCount)
testcafe.close()
})
.catch(error => {
console.log("An ERROR detected:" + error)
})
This is how you install chrome on Dockerfile. Can someone tell me how to install Firefox on Dockerfile?
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && \
http_proxy=${http_proxy} https_proxy=${https_proxy} apt-get update && \
http_proxy=${http_proxy} https_proxy=${https_proxy} apt-get install -y --allow-unauthenticated google-chrome-stable && \
apt clean && rm -rf /var/lib/apt/lists/*
It's impossible to meet all requirements at once.
1) For example, 1 instance for chrome and 1 instance for safari but all tests will run before closing the browser.
You cannot install the Chrome and Safari web browsers on docker image. It's possible to install only Chromium and Firefox on it. See the Using TestCafe docker help topic for more information.
2) If a test fail, I want the screen shot taken and count number of test failed. But do want to continue.
TestCafe's Live mode works in the same way, but it's not available on docker.
This case you need to use Session Handling
During test execution, the Selenium WebDriver has to interact with the browser all the time to execute given commands. At the time of execution, it is also possible that, before current execution completes, someone else starts execution of another script, in the same machine and in the same type of browser.
more details

Node.js on Windows Git Bash shebang failure

Windows Git Bash specific problem...
Pretty simple script which takes some user input, and does not echo it to the output. Works fine when called like node secret.js but acts strange when called as ./secret.js, needing a ctrl+c to exit, and echoing the output as you type.
#!/usr/bin/env node
var prompt = require('prompt');
prompt.start();
prompt.colors = false;
prompt.message = '';
prompt.delimiter = '';
prompt.get([{
name: 'secret',
description: 'tell me your darkest secret: ',
hidden: true
}], function(err, result){
console.log('Hey guys! He said "' + result.secret.slice(0, 5) + '..." only kidding, I won\'t tell.');
});
What is a safe way to make script run on all platforms, including git bash?
update: added env result in case it is useful...
IEUser#ie8winxp MINGW32 ~/projects/issue (develop)
$ env
HOMEPATH=\Documents and Settings\IEUser
MANPATH=/mingw32/share/man:/usr/local/man:/usr/share/man:/usr/man:/share/man:
APPDATA=C:\Documents and Settings\IEUser\Application Data
HOSTNAME=ie8winxp
SHELL=/usr/bin/bash
TERM=xterm
PROCESSOR_IDENTIFIER=x86 Family 6 Model 23 Stepping 10, GenuineIntel
WINDIR=C:\WINDOWS
TMPDIR=/tmp
OLDPWD=/c/Documents and Settings/IEUser/projects
USERDOMAIN=IE8WINXP
OS=Windows_NT
ALLUSERSPROFILE=C:\Documents and Settings\All Users
TEMP=/tmp
COMMONPROGRAMFILES=C:\Program Files\Common Files
USERNAME=IEUser
PROCESSOR_LEVEL=6
PATH=C:\Documents and Settings\IEUser\projects\issuemd\node_modules\.bin:C:\Documents and Settings\IEUser\projects\issue\node_modules\.bin:C:\Documents and Settings\IEUser\projects\node_modules\.bin:/c/Documents and Settings/IEUser/bin:/mingw32/bin:/usr/local/bin:/usr/bin:/bin:/mingw32/bin:/usr/bin:/c/Documents and Settings/IEUser/bin:/c/WINDOWS/system32:/c/WINDOWS:/c/WINDOWS/System32/Wbem:/c/Program Files/nodejs:/c/Documents and Settings/IEUser/Application Data/npm:/usr/bin/vendor_perl:/usr/bin/core_perl
EXEPATH=C:\Program Files\Git
FP_NO_HOST_CHECK=NO
PWD=/c/Documents and Settings/IEUser/projects/issue
SYSTEMDRIVE=C:
LANG=en_US.UTF-8
USERPROFILE=C:\Documents and Settings\IEUser
CLIENTNAME=Console
PS1=\[\033]0;$TITLEPREFIX:${PWD//[^[:ascii:]]/?}\007\]\n\[\033[32m\]\u#\h \[\033[35m\]$MSYSTEM \[\033[33m\]\w\[\033[36m\]`__git_ps1`\[\033[0m\]\n$
LOGONSERVER=\\IE8WINXP
PROCESSOR_ARCHITECTURE=x86
SSH_ASKPASS=/mingw32/libexec/git-core/git-gui--askpass
SHLVL=1
HOME=/c/Documents and Settings/IEUser
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
PLINK_PROTOCOL=ssh
HOMEDRIVE=C:
MSYSTEM=MINGW32
COMSPEC=C:\WINDOWS\system32\cmd.exe
TMP=/tmp
SYSTEMROOT=C:\WINDOWS
PROCESSOR_REVISION=170a
PKG_CONFIG_PATH=/mingw32/lib/pkgconfig:/mingw32/share/pkgconfig
ACLOCAL_PATH=/mingw32/share/aclocal:/usr/share/aclocal
INFOPATH=/usr/local/info:/usr/share/info:/usr/info:/share/info:
PROGRAMFILES=C:\Program Files
DISPLAY=needs-to-be-defined
NUMBER_OF_PROCESSORS=1
SESSIONNAME=Console
COMPUTERNAME=IE8WINXP
_=/usr/bin/env
Turns out cygwin is not supported by node (and I assume git bash too).
Seems that git bash is not a real tty.
Looks like someone did something about it by bundling winpty with git bash.
Solution...
From within git bash, run winpty bash, then rest should work as expected.

Installing Node.JS on Centos 5.5

I'm trying to install Node.js on Centos 5.5, which the latest update has removed yum.
So when i'm trying to run make I get the following error. Any ideas why, or what is going on?
make -C out BUILDTYPE=Release V=1
make[1]: Entering directory `/usr/local/src/node-v0.12.0/out'
LD_LIBRARY_PATH=/usr/local/src/node-v0.12.0/out/Release/lib.host:/usr/local/src/node-v0.12.0/out/Release/lib.target:$LD_LIBRARY_PATH; export LD_LIBRARY_PATH; cd ../deps/v8/tools/gyp; mkdir -p /usr/local/src/node-v0.12.0/out/Release/obj/gen; python ../../tools/js2c.py "/usr/local/src/node-v0.12.0/out/Release/obj/gen/libraries.cc" CORE off ../../src/runtime.js ../../src/v8natives.js ../../src/symbol.js ../../src/array.js ../../src/string.js ../../src/uri.js ../../third_party/fdlibm/fdlibm.js ../../src/math.js ../../src/messages.js ../../src/apinatives.js ../../src/debug-debugger.js ../../src/mirror-debugger.js ../../src/liveedit-debugger.js ../../src/date.js ../../src/json.js ../../src/regexp.js ../../src/arraybuffer.js ../../src/typedarray.js ../../src/weak_collection.js ../../src/promise.js ../../src/object-observe.js ../../src/collection.js ../../src/collection-iterator.js ../../src/macros.py ../../src/array-iterator.js ../../src/string-iterator.js
File "../../tools/js2c.py", line 409
except Error as e:
^
SyntaxError: invalid syntax
make[1]: *** [/usr/local/src/node-v0.12.0/out/Release/obj/gen/libraries.cc] Error 1
make[1]: Leaving directory `/usr/local/src/node-v0.12.0/out'
make: *** [node] Error 2
I'm encountering the same issue on RHEL5. I found a work-around until this gets fixed upstream. Edit deps/v8/tools/gyp/v8.gyp and replace 'python' with 'python2.7' to force that stage of the compile to use the correct version of python.
I filed an upstream bug at https://github.com/joyent/node/issues/9217

Result of ps au | grep ssh different in Node.js (using spawn/pipe) vs shell

I'm playing around with node streams and child processes. So I want to emulate next shell command with pipes:
ps au | grep ssh
So I wrote next code:
var spawn = require('child_process').spawn;
var ps = spawn('ps', ['au']);
var grep = spawn('grep', ['ssh']);
ps.stdout.pipe(grep.stdin);
grep.stdout.on('data', function(data) { console.log(data) });
Then I run it, but nothing happens. What did I do wrong?
P.S. - I know about:
require('child_process')
.exec('ps au | grep ssh', function(err, stdout, stderr) {
...
}).
I'm just playing around with Node.js, and I want to understand what's wrong with this example.
Update 1:
It appeared that with grep bash program works as expected, but with grep ssh there is no result. Although ps au | grep ssh gives me this result:
vagrant 11681 0.0 0.1 10464 916 pts/0 S+ 07:54 0:00 grep --color=auto ssh.
When you call ps it will list all currently running processes matching the passed options. Which might look for ps au something like this:
tniese 3251 0,0 0,0 2479028 3004 s000 S+ 4:06am 0:00.03 -bash
root 4453 0,0 0,0 2452408 876 s004 R+ 4:06pm 0:00.00 ps au
When you call ps au | grep ssh in the shell grep will filter that result to only show the lines containing ssh.
If the grep is launched by the shell before ps creates its listing then the output before the filtering would be:
tniese 3251 0,0 0,0 2479028 3004 s000 S+ 4:06am 0:00.03 -bash
root 4453 0,0 0,0 2452408 876 s004 R+ 4:06pm 0:00.00 ps au
tniese 4478 0,0 0,0 2441988 596 s000 R+ 4:06pm 0:00.00 grep ssh
The grep process will match its own entry as it contains the passed parameters, so the filtered result would be:
tniese 4478 0,0 0,0 2441988 596 s000 R+ 4:06pm 0:00.00 grep ssh
Lets look what is happening with your code:
var spawn = require('child_process').spawn;
var ps = spawn('ps', ['au']);
var grep = spawn('grep', ['ssh']);
ps.stdout.pipe(grep.stdin);
With spawn you tell the OS to start the process ps, ps does not need to wait to run until the output could be piped to anyplace but could start before that, it might only be forced to wait when it tries to write to the its output stream. Then your spawn grep, but at the time grep is launched ps might already created the process listing internally and cause of that it does not contain the grep process. The output of ps is then passed to grep. But as this output is missing grep ssh it won't show that line.
Wether grep will appear in your listing or not is highly OS dependent. Generally you should assume that it is random if it is listed or not. Or you would need to wait until ps exits and launch grep after that.
You need to always keep in mind that current OS have preemptive multitasking and that the scheduler might pause node right after spawn('ps', ['au']); and continue the process right after ps created/requested the listing.
I hope this explanation is a bit clearer then my comment.
I've spawned grep before ps and now it works well. I think it must be a timing issue. Try this.
var spawn = require('child_process').spawn;
var grep = spawn('grep', ['ssh']);
var ps = spawn('ps', ['au']);
ps.stdout.pipe(grep.stdin);
grep.stdout.on('data', function(data) {
console.log(data.toString("utf8"));
});

Categories