Introduction
All people know that if we call undefined.test we will receive the following error (same for both: NodeJS and Javascript):
$ node
> undefined.test
TypeError: Cannot read property 'test' of undefined
at repl:1:11
at REPLServer.self.eval (repl.js:110:21)
at Interface.<anonymous> (repl.js:239:12)
at Interface.EventEmitter.emit (events.js:95:17)
at Interface._onLine (readline.js:202:10)
at Interface._line (readline.js:531:8)
at Interface._ttyWrite (readline.js:760:14)
at ReadStream.onkeypress (readline.js:99:10)
at ReadStream.EventEmitter.emit (events.js:98:17)
at emitKey (readline.js:1095:12)
That's correct!
How did I find the problem?
Passed week I wasted about 30 minutes in debugging the following problem: A script was stopping accidentally and no error was thrown.
I had the urls variable that was supposed to be an object:
var urls = settings.urls || {};
Then in next lines I needed to get shop key of urls that was a string:
var shop = urls.shop || "/";
I started adding console.log to find the values of variables:
console.log(urls); // undefined
var shop = urls.shop || "/";
console.log("Passed"); // This isn't run.
The problem in my script was that I was redefining a new urls variable that was making the urls undefined, but the question is: why cannot read property "shop" of undefined didn't appear here? Because urls was really undefined.
We know that the following is happening in both: NodeJS and Javascript:
var a = 10;
foo(function () {
console.log(a); // undefined
var a = 10;
});
function foo(callback) { callback(); }
The question
After debugging the problem I found that this problem comes from Mongo: inside of Mongo callbacks if we call undefined.something we DON'T get the error.
I've created a small script that demonstrates this:
var mongo = require("mongodb");
// Mongo server
var server = mongo.Server("127.0.0.1", 27017);
var db = new mongo.Db("test", server, { safe: true });
console.log("> START");
// Open database
console.log("Opening database.");
db.open(function(err, db) {
if (err) { return console.log("Cannot open database."); }
// get collection
console.log("No error. Opening collection.");
db.collection("col_one", function(err, collection) {
if(err) { return console.log(err) }
// do something with the collection
console.log("No error. Finding all items from collection.");
collection.find().toArray(function(err, items) {
if(err) { return console.log(err) }
console.log("No error. Items: ", items);
console.log("The following line is: undefined.shop." +
"It will stop the process.");
console.log(undefined.test); // THE ERROR DOES NOT APPEAR!
console.log("> STOP"); // this message doesn't appear.
});
});
});
My questions are:
Why the error doesn't appear? Which is the reason? (It would be great to debug together the MongoDB source code to find it.)
Why the process is stopped when calling undefined.something?
How can be this solved?
I've created a Github repository where you can download my small application that demonstrates the issue.
Interesting:
If we add a try {} catch (e) {} statement we find the error and the process continue showing the STOP message.
try {
console.log(undefined.test);
} catch (e) {
console.log(e);
}
LOGS:
> START
Opening database.
No error. Opening collection.
No error. Finding all items from collection.
No error. Items: []
The following line is: undefined.shop. It will stop the process.
[TypeError: Cannot read property 'test' of undefined]
> STOP
Looking on github.com at node-mongodb-native driver issues, you will notice that issue is solved in 1.3.18 version. But, I tested it and it does not work as expected.
Related
I am getting the dreaded Cannot access 'server' before initialization error in code that is identical to code that's running in production.
The only things that have changed are my OS version (macOS 10.11->10.14) my NodeJS version (10->12) and my VSCode launch.json, but I cannot see anything in either that would cause an issue. My Node version went from 10 to 12, but in production it went from 8 to 15 without issue. I routinely keep launch.json pretty sparse, and the same error happens using node server in Terminal.
Here is the offending code. The issue occurs because I have shutdown() defined before server and it references server. It's written to add an event-handler and then cause the event. Yes, it could be refactored but it already works. It works, really. In 21 instances spread over 7 servers.
I have tried changing the declaraion/init of server from const to var but that does not fix it. As mentioned, this is code that's running in prod! What's wrong with my environment?
Maybe a better question is: why did this ever work?
'use strict'
const fs = require('fs');
const https = require('https');
const cyp = require('crypto').constants;
const stoppable = require('./common/stoppable.js');
const hu = require('./common/hostutil');
process.on('uncaughtException', err => {
wslog.error(`Uncaught Exception: ${err} ${err.stack}`);
shutdown();
});
process.on('unhandledRejection', (reason, p) => {
wslog.error(`Unhandled Promise Rejection: ${reason} - ${p}`);
});
// 'shutdown' is a known static string sent from node-windows wrapper.js if the service is stopped
process.on('message', m => {
if (m == 'shutdown') {
wslog.info(`${wsconfig.appName} has received shutdown message`);
shutdown();
}
});
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
process.on('SIGHUP', shutdown);
function shutdown() {
httpStatus = 503; // Unavailable
wslog.info(`${wsconfig.appName} httpStatus now ${httpStatus} - stopping server...`);
// Error happens on this next line; It should not execute till after server is running already
server.on('close', function () {
wslog.info(`${wsconfig.appName} HTTP server has stopped, now exiting process.`);
process.exit(0)
});
server.stop();
}
// Init and start the web server/listener
var combiCertFile = fs.readFileSync(wsconfig.keyFile, 'utf8');
var certAuthorityFile = fs.readFileSync(wsconfig.caFile, 'utf8');
var serverOptions = {
key: combiCertFile,
cert: combiCertFile,
ca: certAuthorityFile,
passphrase: wsconfig.certPass,
secureOptions: cyp.SSL_OP_NO_TLSv1 | cyp.SSL_OP_NO_TLSv1_1
};
var server = https.createServer(serverOptions, global.app)
.listen(wsconfig.port, function () {
wslog.info(`listening on port ${wsconfig.port}.`);
});
server.on('clientError', (err, socket) => {
if (err.code === 'ECONNRESET' || !socket.writable) { return; }
// ECONNRESET was already logged in socket.on.error. Here, we log others.
wslog.warn(`Client error: ${err} ${err.stack}`);
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.on('error', (err)=>{
if ( err.code === 'EADDRINUSE' ) {
wslog.error(`${err.code} FATAL - Another ${wsconfig.appName} or app is using my port! ${wsconfig.port}`);
} else {
wslog.error(`${err.code} FATAL - Server error: ${err.stack}`);
}
shutdown();
})
combiCertFile = null;
certAuthorityFile = null;
// Post-instantiation configuration required (may differ between apps: need an indirect way to plug in app-specific behavior)
stoppable(server, wsconfig.stopTimeout);
// Load all RESTful endpoints
const routes = require('./routes/');
This is a runtime error, which happens only in a very specific situation. But actually this exact error shouldn't happen with var server = ... but only with const server = ... or let server = .... With var server = ... the error message should say "Cannot read properties of undefined"
What happens
You have an error handler for uncaughtException which is calling shutdown() and in shutdown() you are referencing your server. But consider what happens if your code throws an exception before you initialized your server. For instance if your cert or key cannot be read from the disk, cert or key are invalid ... So nothing will be assigned to server, and an exception will be raised.
Then the handler for your uncaught exception will fire and call the shutdown() function, which then tries to access the server, which of course hasn't been initialized yet.
How to fix
Check what the unhandled exception is, that is thrown before your server is initialized and fix it. In your production environment, there is probably no exception, because the configuration and environment is properly set up. But there is at least one issue in your develepment environment, which causes an exception.
Difference between var and const
And the difference between var server = ... and const server = ... is quite a subtle one. For both, the declaration of the variable is hoisted up to the top of their respective scope. In your case it's always global, also for const. But variables declared as var are assigned a value of undefined whereas variables declared as let/const are not initialized at all.
You can easily reproduce this error if you uncomment either error1 or error2 in the following code. But error3 alone won't produce this ReferenceError because bar will already be initialized. You can also replace const bar = with var bar = and you will see, that you get a different error message.
process.on("uncaughtException", err => {
console.log("uncaught exception");
console.log(err);
foo();
});
function foo() {
console.log("foo");
console.log(bar.name);
}
function init() {
// throw new Error("error1");
return { name: "foobar"}
}
// throw new Error("error2");
const bar = init();
//throw new Error("error3");
I'm creating a REST API to store setting from a specific camera sending data through TCP.
Camera communicate through TCP with a request/response pattern.
e.g : You can send "Get rate\n" to the camera and it responds "100 fps", in order to get the current framerate of the camera.
How to get data and store it in API?
var command = "rate"; //Array with setting I want to store in my API
function getDataFromCamera(command) { // This function will be iterate in another function
async.series([
console.log('Test1'); // console log to test output
function(callback) {
CAMERA_TCP_SOCKET.send("get "+command+"\n");
callback(null,command);
},
function(callback) {
console.log('Test2'); // console log to test output
CAMERA_TCP_SOCKET.onmessage = function(event) {
callback(null, event.data); // THIS line is the problem. I can't retrieve event.data because i don't know how to callback this variable
}
}],
function(err, results) {
if (err) {
//Handle the error in some way. Here we simply throw it
throw err;
}
if (results) {
console.log(results); // expected output : // ['rate','100']
}
});
}
At the moment, I get this error:
Test1
Test2
/Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:966
if (fn === null) throw new Error("Callback was already called.");
^
Error: Callback was already called.
at /Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:966:32
at /Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:3885:13
at W3CWebSocket.CAMERA_TCP_SOCKET.onmessage (/Users/maximecongi/Desktop/Hologram/core/api.js:91:13)
at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/maximecongi/Desktop/Hologram/node_modules/yaeti/lib/EventTarget.js:107:17)
at W3CWebSocket.onMessage (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/W3CWebSocket.js:234:14)
at WebSocketConnection.<anonymous> (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/W3CWebSocket.js:205:19)
at WebSocketConnection.emit (events.js:188:13)
at WebSocketConnection.processFrame (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/WebSocketConnection.js:554:26)
at /Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/WebSocketConnection.js:323:40
at process.internalTickCallback (internal/process/next_tick.js:70:11)
[nodemon] app crashed - waiting for file changes before starting...
How to solve my problem?
It's because you are listening on every message, and looks like you're sending 3 commands, and I guess, every command get a response. This callback after first serie is not destroyed, its alive. For me, you should check if the response is for this specific command, then execute the problematic callback.
Node PhantomJs throwing errors for 75+ concurrent requests , number of requests are bit irrelevant here.It's just issue is exposed at that point.
I'm suspecting either requests are timing out or underlying sockets not playing well with nodeJs.I'm trying to pinpoint issue and would definitely appreciate any help.
Error logs :
phantom stdout: :67 in onclose
phantom stdout: [WDS] Disconnected!
phantom stdout: :67 in onclose
phantom stdout: TypeError: undefined is not an object (evaluating 'parent._jp.anxzqzn')
http://localhost:9000/sockjs-node/592/4vgdvqkc/htmlfile?c=_jp.anxzqzn:8 in global code
TypeError: undefined is not an object (evaluating 'c.message')
http://localhost:9000/sockjs-node/592/4vgdvqkc/htmlfile?c=_jp.anxzqzn:10 in p
phantom stdout: TypeError: undefined is not an object (evaluating 'c.message')
http://localhost:9000/sockjs-node/592/4vgdvqkc/htmlfile?c=_jp.anxzqzn:10 in p
TypeError: undefined is not an object (evaluating 'c.message')
http://localhost:9000/sockjs-node/592/4vgdvqkc/htmlfile?c=_jp.anxzqzn:10 in p
Node Server:
^0.10.40
5.3.0 stable
Tried with both node versions to verify ; it's not a NodeJs issue
Code Snippet :
page.open(decodedURL, function (status) {
if (status !== "success") {
console.log("phantom app failed to respond");
response.statusCode = 400;
response.write('Unable to load url: ' + decodedURL);
response.end();
//Close page object as we're returning response
console.log("closing objects");
ph.exit()
//phantom.exit
} else {
//Wait for all callback from the phantom app before rendering
console.log("Waiting for callback");
page.set('onCallback', function () {
//console.log("Callback name"+this.name);
console.log("received callback from app so rendering now");
renderImages();
/*else {
responseImages.push(page.renderBase64());
ph.exit()
}
*/
});
// Otherwise wait for maxTimeout, unless invoked earlier by callback from app
//If we reach the max timeout we throw an error and just return
//This is because we don't want to return half loaded images back to the caller
//garbage collect page as well
var forceRenderTimeout = setTimeout(function () {
console.log("Reached the maximum timeout so returning 400");
response.statusCode = 400;
response.write('Unable to load url: ' + decodedURL);
response.end()
console.log("closing objects");
ph.exit()
//phantom.exit
// page.close();
}, defaultOpts.maxTimeout);
}
});
PhantomJs: github.com/ariya/phantomjs
PhantomJs_Node: github.com/sgentle/phantomjs-node
Callback mechanism being used in current code : phantomjs.org/api/webpage/handler/on-callback.html
I have used pouchDB in one application and now I want to introduce couchDB to sync the document to remote server. Hence i followed this link http://pouchdb.com/getting-started.html i used the below code to replicate the data to couchDB
var db2 = new PouchDB('todos');
var remoteCouch = 'http://localhost:5984/_utils/database.html?couchdb_sample';
db2.changes({
since: 'now',
live: true
}).on('change', showTodos);
sync();
function sync() {
//alert("sync");
//syncDom.setAttribute('data-sync-state', 'syncing');
//var opts = {live: true};
db2.replicate.to(remoteCouch).on('complete', function () {
console.log("done");
}).on('error', function (err) {
console.log(err);
});
function addTodo(text) {
var todo = {
_id: $("#eid").val()+$("#version").val(),
title: text,
name: $("#nameid").val(),
version: $("#version").val(),
completed: false
};
db2.put(todo, function callback(err, result) {
if (!err) {
console.log('Successfully posted a todo!');
}
else{
console.log(err);
}
});}
here the title has an xml string as value. But i am facing below error
SyntaxError: Unexpected token <
at Object.parse (native)
for this line db2.replicate.to(remoteCouch). I manually created a new document in couchDb database and entered the same data it gave no error but when i try replicating it shows syntax error. Can anyone please hint me where I have gone wrong
http://localhost:5984/_utils/database.html?couchdb_sample
Points to a HTML site (copied over from the browsers address bar, right?). Remove the middle part:
http://localhost:5984/couchdb_sample
It look like you have not defined the remote database in the way PouchDb is expecting. You should use the "new PouchDb" call. The second line of your code is:
var remoteCouch = 'http://localhost:5984/_utils/database.html?couchdb_sample';
but I think it should be like this:
var remoteCouch = new PouchDB('http://localhost:5984/couchdb_sample');
I am not clear from your code what the name of the remote database is, but it would not normally end in ".html" as Ingo Radatz pointed out, so I have assumed it is couchdb_sample above. There is more information about replication on the PouchDb site.
I have the following code in server/statusboard.js;
var require = __meteor_bootstrap__.require,
request = require("request")
function getServices(services) {
services = [];
request('http://some-server/vshell/index.php?type=services&mode=json', function (error, response, body) {
var resJSON = JSON.parse(body);
_.each(resJSON, function(data) {
var host = data["host_name"];
var service = data["service_description"];
var hardState = data["last_hard_state"];
var currState = data["current_state"];
services+={host: host, service: service, hardState: hardState, currState: currState};
Services.insert({host: host, service: service, hardState: hardState, currState: currState});
});
});
}
Meteor.startup(function () {
var services = [];
getServices(services);
console.log(services);
});
Basically, it's pulling some data from a JSON feed and trying to push it into a collection.
When I start up Meteor I get the following exception;
app/packages/livedata/livedata_server.js:781
throw exception;
^
Error: Meteor code must always run within a Fiber
at [object Object].withValue (app/packages/meteor/dynamics_nodejs.js:22:15)
at [object Object].apply (app/packages/livedata/livedata_server.js:767:45)
at [object Object].insert (app/packages/mongo-livedata/collection.js:199:21)
at app/server/statusboard.js:15:16
at Array.forEach (native)
at Function.<anonymous> (app/packages/underscore/underscore.js:76:11)
at Request._callback (app/server/statusboard.js:9:7)
at Request.callback (/usr/local/meteor/lib/node_modules/request/main.js:108:22)
at Request.<anonymous> (/usr/local/meteor/lib/node_modules/request/main.js:468:18)
at Request.emit (events.js:67:17)
Exited with code: 1
I'm not too sure what that error means. Does anyone have any ideas, or can suggest a different approach?
Just wrapping your function in a Fiber might not be enough and can lead to unexpected behavior.
The reason is, along with Fiber, Meteor requires a set of variables attached to a fiber. Meteor uses data attached to a fiber as a dynamic scope and the easiest way to use it with 3rd party api is to use Meteor.bindEnvironment.
T.post('someurl', Meteor.bindEnvironment(function (err, res) {
// do stuff
// can access Meteor.userId
// still have MongoDB write fence
}, function () { console.log('Failed to bind environment'); }));
Watch these videos on evented mind if you want to know more:
https://www.eventedmind.com/posts/meteor-dynamic-scoping-with-environment-variables
https://www.eventedmind.com/posts/meteor-what-is-meteor-bindenvironment
As mentioned above it is because your executing code within a callback.
Any code you're running on the server-side needs to be contained within a Fiber.
Try changing your getServices function to look like this:
function getServices(services) {
Fiber(function() {
services = [];
request('http://some-server/vshell/index.php?type=services&mode=json', function (error, response, body) {
var resJSON = JSON.parse(body);
_.each(resJSON, function(data) {
var host = data["host_name"];
var service = data["service_description"];
var hardState = data["last_hard_state"];
var currState = data["current_state"];
services+={host: host, service: service, hardState: hardState, currState: currState};
Services.insert({host: host, service: service, hardState: hardState, currState: currState});
});
});
}).run();
}
I just ran into a similar problem and this worked for me. What I have to say though is that I am very new to this and I do not know if this is how this should be done.
You probably could get away with only wrapping your insert statement in the Fiber, but I am not positive.
Based on my tests you have to wrap the insert in code I tested that is similar to the above example.
For example, I did this and it still failed with Fibers error.
function insertPost(args) {
if(args) {
Fiber(function() {
post_text = args.text.slice(0,140);
T.post('statuses/update', { status: post_text },
function(err, reply) {
if(reply){
// TODO remove console output
console.log('reply: ' + JSON.stringify(reply,0,4));
console.log('incoming twitter string: ' + reply.id_str);
// TODO insert record
var ts = Date.now();
id = Posts.insert({
post: post_text,
twitter_id_str: reply.id_str,
created: ts
});
}else {
console.log('error: ' + JSON.stringify(err,0,4));
// TODO maybe store locally even though it failed on twitter
// and run service in background to push them later?
}
}
);
}).run();
}
}
I did this and it ran fine with no errors.
function insertPost(args) {
if(args) {
post_text = args.text.slice(0,140);
T.post('statuses/update', { status: post_text },
function(err, reply) {
if(reply){
// TODO remove console output
console.log('reply: ' + JSON.stringify(reply,0,4));
console.log('incoming twitter string: ' + reply.id_str);
// TODO insert record
var ts = Date.now();
Fiber(function() {
id = Posts.insert({
post: post_text,
twitter_id_str: reply.id_str,
created: ts
});
}).run();
}else {
console.log('error: ' + JSON.stringify(err,0,4));
// TODO maybe store locally even though it failed on twitter
// and run service in background to push them later?
}
}
);
}
}
I thought this might help others encountering this issue. I have not yet tested calling the asynchy type of external service after internal code and wrapping that in a Fiber. That might be worth testing as well. In my case I needed to know the remote action happened before I do my local action.
Hope this contributes to this question thread.