Node.js Bluebird Promise complains of "Converting circular structure to JSON" - javascript

For making a outgoing api call, I am using restler wrapped in a bluebird promise object. However, I am getting a Possibly unhandled TypeError: Converting circular structure to JSON at node_modules/bluebird/js/main/promise.js.
This happens even without JSON.stringify. It is happening at the level of promise.js.
What can I do about this?
rest.postJson(uri, body).then(
function(response) {
console.log(response.access.token.id);
console.log(response.statusCode);
res.send(response.access.token.id);
}, function(error) {
console.log(error.statusCode);
res.send(JSON.stringify(error));
//res.send(error);
});
Possibly unhandled TypeError: Converting circular structure to JSON
at Object.stringify (native)
at ServerResponse.res.json (/home/one/try/cloudimageshare-monitoring/project/node_modules/express/lib/response.js:205:19)
at ServerResponse.res.send (/home/one/try/cloudimageshare-monitoring/project/node_modules/express/lib/response.js:121:21)
at /home/one/try/cloudimageshare-monitoring/project/app/scripts/proxy/proxies_express_module.js:39:29
at tryCatch1 (/home/one/try/cloudimageshare-monitoring/project/node_modules/bluebird/js/main/util.js:43:21)
at Promise$_callHandler [as _callHandler] (/home/one/try/cloudimageshare-monitoring/project/node_modules/bluebird/js/main/promise.js:627:13)
at Promise$_settlePromiseFromHandler [as _settlePromiseFromHandler] (/home/one/try/cloudimageshare-monitoring/project/node_modules/bluebird/js/main/promise.js:641:18)
at Promise$_settlePromiseAt [as _settlePromiseAt] (/home/one/try/cloudimageshare-monitoring/project/node_modules/bluebird/js/main/promise.js:804:14)
at Promise$_settlePromises [as _settlePromises] (/home/one/try/cloudimageshare-monitoring/project/node_modules/bluebird/js/main/promise.js:938:14)
at Promise$_rejectPromises [as _rejectPromises] (/home/one/try/cloudimageshare-monitoring/project/node_modules/bluebird/js/main/promise.js:931:10)
UPDATE: Here are the keys of the error object if it helps any:
[ '_readableState',
'readable',
'domain',
'_events',
'_maxListeners',
'socket',
'connection',
'httpVersion',
'complete',
'headers',
'trailers',
'_pendings',
'_pendingIndex',
'url',
'method',
'statusCode',
'client',
'_consuming',
'_dumped',
'httpVersionMajor',
'httpVersionMinor',
'upgrade',
'req',
'pipe',
'addListener',
'on',
'pause',
'resume',
'read',
'rawEncoded',
'raw' ]

No. When you don't pass a string to express' res.send but an object (like error), it will call JSON.stringify internally. This will for sure not happen in promise.js (however, Bluebird does weird things to the stacktrace). Try
res.send(error.message);

Related

Getting the right stack trace out of jest

I am currently debugging some tests written with jest over typescript and I'm having a bit of a headache.
If a test, or tested class, runs Postgres SQL and there is an error in the query, I get the wrong stack trace, for example, this:
error: invalid input syntax for type integer: ""0""
at Parser.parseErrorMessage (/Users/sklivvz/src/xxx/node_modules/pg-protocol/src/parser.ts:369:69)
at Parser.handlePacket (/Users/sklivvz/src/xxx/node_modules/pg-protocol/src/parser.ts:188:21)
at Parser.parse (/Users/sklivvz/src/xxx/node_modules/pg-protocol/src/parser.ts:103:30)
at Socket.<anonymous> (/Users/sklivvz/src/xxx/node_modules/pg-protocol/src/index.ts:7:48)
at Socket.emit (node:events:365:28)
at addChunk (node:internal/streams/readable:314:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at Socket.Readable.push (node:internal/streams/readable:228:10)
at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
The "error" line is very useful, however, the stack trace only tells me that the error was thrown by the pg-protocol driver. I would like to know which line within my code generated the error.
I am exactly 82.7% sure that this is due to PG's query being async.
It is incredibly time-consuming having to step debug or (gasp) console.log my way to each error when it would only be a matter of showing the correct call stack in order to make it better.
Has anyone found a way of making this developer-friendly?
Check if this is related to brianc/node-postgres issue 2484
is (there) a preferred package, extension, or method for providing more detail when you get a syntax error back from the parser?
(for instance, one that listed line number, column of the error)
for instance, right now:
error: syntax error at or near "as"
at Parser.parseErrorMessage (/home/collspec/projects/staff-portal/sprint-server/node_modules/pg-protocol/dist/parser.js:278:15)
desired behavior:
error: syntax error at or near "as", line 5, column 7
at Parser.parseErrorMessage (/home/collspec/projects/staff-portal/sprint-server/node_modules/pg-protocol/dist/parser.js:278:15)
Possible workaround from that issue:
There are a bunch of additional fields on Error objects populated by the driver.
If you log the error object you can see them. They correspond to the error fields returned by the server:
For example with the command:
SELECT foo
FROM bar
You can get an error like this:
{
length: 102,
severity: 'ERROR',
code: '42P01',
detail: undefined,
hint: undefined,
position: '17',
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file: 'parse_relation.c',
line: '1180',
routine: 'parserOpenTable'
}
The one you want is position. It gives you the character offset in the SQL of the error.
In this example the position value of "17" refers to the start of the bar token in the SQL.
It's not always populated though as it depends on what caused the error (generally just parse errors).
I ran into a similar issue with aws-sdk for DynamoDb. This is a stacktrace I usually get from aws-sdk.
ResourceNotFoundException: Requested resource not found
at Request.extractError (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\protocol\json.js:52:27)
at Request.callListeners (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\sequential_executor.js:106:20)
at Request.emit (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\sequential_executor.js:78:10)
at Request.emit (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\request.js:688:14)
at Request.transition (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\request.js:22:10)
at AcceptorStateMachine.runTo (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\state_machine.js:14:12)
at D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\state_machine.js:26:10
at Request.<anonymous> (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\request.js:38:9)
at Request.<anonymous> (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\request.js:690:12)
at Request.callListeners (D:\workspaces\typescript-starters\console-app\node_modules\aws-sdk\lib\sequential_executor.js:116:18)
My workaround is simply to catch async errors, and overwrite their stack traces. On the other hand, you may append Postgres stacktrace, or error message to your own errors.
async function getPersonFromDb (personId: string): Promise<DocumentClient.AttributeMap | undefined> {
const result = await documentClient.get({ // Similar to postgres.query()
TableName: 'wrong-name',
Key: { pk: personId, sk: personId }
}).promise().catch(error => {
Error.captureStackTrace(error)
throw error
})
return result.Item
}
test('Get a person from DynamoDB', async () => {
const person = await getPersonFromDb('hello')
expect(person).not.toBeUndefined()
})
// ========= new stacktrace ========
Error: Requested resource not found
at D:\workspaces\typescript-starters\console-app\test\abc.test.ts:12:13
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at getPersonFromDb (D:\workspaces\typescript-starters\console-app\test\abc.test.ts:8:20)
at Object.<anonymous> (D:\workspaces\typescript-starters\console-app\test\abc.test.ts:18:20) // my code, and where my error is thrown

JavaScript ES6 - console.log with template literal

I'm writing node's script that connect to the mongo database.
I noticed that printing error response is different by small change in console.log syntax. Below example should be more meaningful.
#!/usr/bin/env node
const mongoose = require("mongoose");
const config = require("./config");
mongoose.connect(config.dbURI, (err) => {
if (err) {
console.log(`${err}`); // First console.log
console.log(err); // Second console.log
} else {
console.log(`Database connection successful`)
}
});
Example output of the first console.log
MongoError: Authentication failed.
Example output of the second console.log
{ MongoError: Authentication failed.
at Function.MongoError.create (/Users/sigo/Sources/crypto-change/node_modules/mongodb-core/lib/error.js:31:11)
at /Users/sigo/Sources/crypto-change/node_modules/mongodb-core/lib/connection/pool.js:489:72
at authenticateStragglers (/Users/sigo/Sources/crypto-change/node_modules/mongodb-core/lib/connection/pool.js:435:16)
at Connection.messageHandler (/Users/sigo/Sources/crypto-change/node_modules/mongodb-core/lib/connection/pool.js:469:5)
at Socket.<anonymous> (/Users/sigo/Sources/crypto-change/node_modules/mongodb-core/lib/connection/connection.js:321:22)
at emitOne (events.js:96:13)
at Socket.emit (events.js:191:7)
at readableAddChunk (_stream_readable.js:178:18)
at Socket.Readable.push (_stream_readable.js:136:10)
at TCP.onread (net.js:563:20)
name: 'MongoError',
message: 'Authentication failed.',
ok: 0,
code: 18,
errmsg: 'Authentication failed.' }
Where comes from this difference?
${err} (or '' + err for that matter) will interpolate the err object with a string which will also change your Error object into a string - which is equivalent to calling err.toString(). However directly logging err will pass it to the console as an object and display it in that manner.
This is called "implicit coercion" - if you would like to know more about this subject I can highly recommend reading You Don't Know JS: Types & Grammar (Chapter 4: Coercion) for a deep dive.
By interpolating in a template string, the err object is cast to a string, using its own .toString method that is inherited from Error.prototype.toString. The code is equivalent to
console.log(String(err));
console.log(err.toString());
When passing the err object directly to the console.log method, it uses node's inspect function instead, the code is equivalent to
console.log(util.inspect(err));

Node.js JSON.parse "Unexpected token {"

I have a script that uses JSON.parse(datastring); to parse a stringified JSON.
datastrings look like this:
{"_id":"8b8fdd243f734b27829c92e4099f70ec.d","date":1439418654920,"player":"player1","action":"capture"}
{"_id":"a3b7d70d8a074f9ba8b13368ee947f1e.d","date":1439418074476,"player":"player1","action":"capture"}
First one works just fine, but with the second one I get a weird error, that I can't find a way to solve.
undefined:2
{"_id":"a3b7d70d8a074f9ba8b13368ee947f1e.d","date": 1439418074476,"player":"pla
^
SyntaxError: Unexpected token {
at Object.parse (native)
at Socket.<anonymous> (/home/ubuntu/workspace/lib/engine.js:12:18)
at Socket.emit (events.js:95:17)
at Socket.<anonymous> (_stream_readable.js:765:14)
at Socket.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:427:10)
at emitReadable (_stream_readable.js:423:5)
at readableAddChunk (_stream_readable.js:166:9)
at Socket.Readable.push (_stream_readable.js:128:10)
at Pipe.onread (net.js:529:21)
Any ideas?
JSON.parse expects to be passed a string containing a JSON text, not a string containing multiple JSON texts.
Either parse each line (assuming you can trust that line breaks will only be between the JSON texts in your input) separately…
var json_texts = datastring.split("\n");
… or express the data in an array to start with.
[
{"_id":"8b8fdd243f734b27829c92e4099f70ec.d","date":1439418654920,"player":"player1","action":"capture"},
{"_id":"a3b7d70d8a074f9ba8b13368ee947f1e.d","date":1439418074476,"player":"player1","action":"capture"}
]

VAST Error: on VASTClient.buildVastTree

I use VideoJS + VAST/VPAID plugin. Getting this error on any VAST/VPAID XML when I try to play Ad:
VIDEOJS: ERROR: (CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED) The video could
not be loaded, either because the server or network failed or because
the format is not supported
I use this and this as sample XML, and some of my own made.
Console output:
AD ERROR: VAST Error: on VASTClient.buildVastTree, error parsing xml z {message: "VAST Error: on VASTClient.buildVastTree, error parsing xml", code: 100}code: 100message: "VAST Error: on VASTClient.buildVastTree, error parsing xml"stack: (...)__proto__: Error
at http://example.com/videojs/vpaid/videojs-vast-vpaid.min.js:2:29160
at http://example.com/videojs/vpaid/videojs-vast-vpaid.min.js:3:6281name: "VAST Error"stack: (...)get stack: ()arguments: nullcaller: nulllength: 0name: ""prototype: aK__proto__: ()<function scope>No Scopesset stack: ()arguments: nullcaller: nulllength: 1name: ""prototype: aN__proto__: ()<function scope>__proto__: DefineError.aTconstructor: Error()message: ""name: "Error"toString: toString()__proto__: Object__defineGetter__: __defineGetter__()__defineSetter__: __defineSetter__()__lookupGetter__: __lookupGetter__()__lookupSetter__: __lookupSetter__()constructor: Object()hasOwnProperty: hasOwnProperty()isPrototypeOf: isPrototypeOf()propertyIsEnumerable: propertyIsEnumerable()toLocaleString: toLocaleString()toString: toString()valueOf: valueOf()get __proto__: get __proto__()set __proto__: set __proto__() undefined
Any ideas how to fix this?
Ok, never mind I've just fixed this by debeautifying all JS library files, possibly it's some kind of parsing errors.

Throwing exception on mongoose callback

I'm developing a Node.js + Express. My database is Mongo, and I'm using Mongoose to connect to this database.
I'm trying to throw an exception in a Mongoose query callback this way:
game.save(function (err) {
if (err) {
throw new app.exception.EntitySaveFailed();
}
});
but when I do it, Node server crashes and shows this stacktrace:
[object Object]
at Promise.<anonymous> (/home/server/routes/api/game.js:219:17)
at Promise.<anonymous> (/home/server/node_modules/mongoose/node_modules/mpromise/lib/promise.js:162:8)
at Promise.EventEmitter.emit (events.js:95:17)
at Promise.emit (/home/server/node_modules/mongoose/node_modules/mpromise/lib/promise.js:79:38)
at Promise.fulfill (/home/server/node_modules/mongoose/node_modules/mpromise/lib/promise.js:92:20)
at Promise.resolve (/home/server/node_modules/mongoose/lib/promise.js:108:15)
at Promise.<anonymous> (/home/server/node_modules/mongoose/node_modules/mpromise/lib/promise.js:162:8)
at Promise.EventEmitter.emit (events.js:95:17)
at Promise.emit (/home/server/node_modules/mongoose/node_modules/mpromise/lib/promise.js:79:38)
at Promise.fulfill (/home/server/node_modules/mongoose/node_modules/mpromise/lib/promise.js:92:20)
Forever detected script exited with code: 8
Forever restarting script for 9389 time
Listening on port 3000...
When I throw an exception outside a mongoose callback function, all works fine.
When you throw an exception in a node.js callback it goes to the code that actually invoked the callback, not the lexical scope of your code. In this case, that means the Promise library receives the exception and crashes your program. This is why errors are typically reported via the first callback parameter rather than exceptions.
You can add "next" to the params of the function, so when you need to throw an exception you can do something like this:
function saveGame(req, res, next) {
game.save(function (err) {
if (err) {
return next(new app.exception.EntitySaveFailed());
}
});
}

Categories