Is this intended behaviour of custom errors? - node.js

I'm currently in the process of remaking the maze package from five years ago in ES2015. I am making a custom error, named LengthError, which will be thrown if an argument of type Function does not have a specified length. I just want to know if this is the intended behaviour because I am running this locally, or if this will carry over to production for when others might use this function?
Error:
LengthError: Argument 'adjacent' must be of length 2
/home/runner/maze/index.ts:6
throw new LengthError('Argument \'adjacent\' must be of length 2')
^
LengthError: Argument 'adjacent' must be of length 2
at null.generate (/home/runner/maze/index.ts:6:13)
at Object.<anonymous> (/home/runner/maze/index.ts:37:1)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:17:47
index.ts:
import { LengthError } from './errors';
export default function generate(nodes: number[], adjacent: Function, choose: Function) {
if (adjacent.length !== 2) {
try {
throw new LengthError('Argument \'adjacent\' must be of length 2')
} catch(e: any) {
console.error(e.name + ': ' + e.message + '\n' + e.stack)
}
}
let node: number = choose(nodes);
let stack = [node];
let maze = new Map();
for (node of nodes) {
maze.set(node, []);
}
while (node) {
let neighbors = nodes.filter(other => !maze.get(other).length && adjacent(node, other));
if (neighbors.length) {
const neighbor = choose(neighbors);
maze.get(node).push(neighbor);
maze.get(neighbor).push(node);
stack.unshift(neighbor);
node = neighbor;
} else {
stack.shift();
node = stack[0];
}
}
return maze;
}
generate([], function a() {}, function b() {});
errors.ts:
class LengthError extends Error {
constructor(message: string) {
super(message);
this.message = message;
this.name = "LengthError";
}
}
export { LengthError };
Again, is this code going to display a similar error in production (where the custom error shows twice) and will it point to the same line in my file?

I just want to know if this is the intended behaviour because I am running this locally, or if this will carry over to production for when others might use this function?
Yes, this is how it works, both locally and in production. This is what nodejs does when there's an uncaught exception using try/catch.
When you throw errors, you're supposed to have code somewhere else that catches them and turns them into the desired behavior.
In the error message, the first line is the statement of the error. The second set of lines are the "stack trace" that show where in the code this originated from, including the current call stack at the time of the error.
Note, in your code that catches exceptions, you may want to log the exception and perhaps even log the track trace and then "handle" the error in some way that makes sense for your application (such as return a user-friendly error message or in an API, return some documented API error or in an http request, return a 4xx or 5xx error status).

Related

How to truncate custom field in (Node)JS error object when console-logging

I have a (Node)JS class:
class PayloadContainingError extends Error {
constructor(msg, payload) {
super(msg);
this.payload = payload;
}
}
payload field may contain long strings, even in MB range.
If I console.log this class at some point, I get the full payload in the log.
Instead, I want it to log a truncated portion (like the Linux head command).
E.g. if I console.error("Bad payload", instance_of_PayloadContainingError), instead of getting
Bad payload { Error: BAD
at foo.bar
payload:
'a possibly million-character long line that pollutes my log'
}
I want console to log
Bad payload { Error: BAD
at foo.bar
payload:
'first 100 chars...'
}
Is this possible via some magic on the class/field level - without having to refactor any (existing and future) console.log calls?
[Edit]
For those voting to close this question in favor of "JavaScript toString() override": per my understanding, toString() is not the question here - console seems to do beyond what toString() usually does, when logging an error object (e.g. adding the stacktrace - which I don't want to reimplement anyway). (As mentioned in one of my comments, overriding toString() does not change the output anyway.)
It seems like node's console.* methods take into account only enumerable properties. Therefore you could make this property non-enumerable:
class PayloadContainingError extends Error {
constructor(msg, payload) {
super(msg);
Object.defineProperty(this, "payload", {
enumerable: false,
value: payload,
});
}
}
const e = new PayloadContainingError("test error", "a possibly million-character long line that pollutes my log");
console.error(e);
$ node ./test.js
PayloadContainingError: test error
at Object.<anonymous> (/home/slava-b/arc/fei-24447/frontend/projects/infratest/packages/tokenator-universal/test.js:43:11)
at Module._compile (internal/modules/cjs/loader.js:956:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
at Module.load (internal/modules/cjs/loader.js:812:32)
at Function.Module._load (internal/modules/cjs/loader.js:724:14)
at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
at internal/main/run_main_module.js:17:11

googleapis-common throwing error in UUID dependency

I'm trying to get a very basic oauth example to work in a node.js app with express and googleapis. Upon running the application it throws a TypeError inside the UUID dependency which is included with the googleapis-common module. I'm getting a bit frustrated at this point because I have not been able to find any additional information about this to allow me to resolve it myself.
Take a look at the screenshot below for the specifics:
Here it is in text if that makes things easier:
Exception has occurred: TypeError: Cannot assign to read only property 'name' of function 'function generateUUID(value, namespace, buf, offset) {
if (typeof value === 'string') {
value = strin...<omitted>... }'
at _default (C:\Users\ficar\OneDrive\Desktop\Frontend\node_modules\googleapis-common\node_modules\uuid\dist\v35.js:71:23)
at Object.<anonymous> (C:\Users\ficar\OneDrive\Desktop\Frontend\node_modules\googleapis-common\node_modules\uuid\dist\v3.js:14:27)
at Module._compile (internal/modules/cjs/loader.js:1137:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
at Module.load (internal/modules/cjs/loader.js:985:32)
at Function.Module._load (internal/modules/cjs/loader.js:878:14)
at Module.require (internal/modules/cjs/loader.js:1025:19)
at require (internal/modules/cjs/helpers.js:72:18)
at Object.<anonymous> (C:\Users\ficar\OneDrive\Desktop\Frontend\node_modules\googleapis-common\node_modules\uuid\dist\index.js:63:34)
at Module._compile (internal/modules/cjs/loader.js:1137:30)
The file this is being thrown in is called "v35.js".
My initial thought is that I must be missing some additional library that interprets the logic throwing the error differently. Eager to learn more about this and find a resolution.
Looks like this is how the uuid module works
node_modules/uuid/dist/v35.js
function _default(name, version, hashfunc) {
function generateUUID(value, namespace, buf, offset) {
...
} // Function#name is not settable on some platforms (#270)
try {
generateUUID.name = name; // eslint-disable-next-line no-empty
} catch (err) {} // For CommonJS default export support
...
Authors warn (comment on line 4), that name property may not be settable and bypass it with empty catch

getting the entire error stacktrace in nodejs

I follow a simple try … catch pattern for my SQLite queries
try {
… run query and get result …
}
catch (error) {
console.log(error);
}
On error, I get a nice stacktrace like this
SqliteError: no such column:
at getData (/Users/punkish/Projects/zenodeo/bin/facets.js:8:25)
at Object.<anonymous> (/Users/punkish/Projects/zenodeo/bin/facets.js:23:1)
at Module._compile (internal/modules/cjs/loader.js:1156:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1176:10)
at Module.load (internal/modules/cjs/loader.js:1000:32)
at Function.Module._load (internal/modules/cjs/loader.js:899:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
at internal/main/run_main_module.js:18:47
I've written my own small logging routine so I can embellish the messages with colors (using chalk) and some extra info I find helpful, now I have
try {
… run query and get result …
}
catch (error) {
mylogger(error);
}
Except, now I just get the following
SqliteError: no such column:
And no, the mylogger is not eating/chopping away the extra bits. I've added the following to it
if (typeof error === 'object') {
log('type: object');
log(`error: ${JSON.stringify(error)}`)
}
and I get
type: object
error: SqliteError: no such column:
Seems like the stacktrace is streaming, and all of it doesn't go to mylogger. In any case, how can I get all of it?
I am using Winston and I encountered the same issue before. I'm using .constructor instead of instanceOf to determine the data type which might be the best practice. But my code here might be of some value to you:
winston​.​format​.​printf​(​info​ ​=>​ {
        ​if​ (​info​.​message​.​constructor​ ​===​ ​Object​ ​||​ ​Error​) {
            ​if​ (​info​.​stack​ ​===​ ​undefined​) {
                ​return​ ​`​${​info​.​timestamp​}​ [​${​info​.​label​}​] ​${​info​.​level​}​: ​${​JSON​.​stringify​(​info​.​message​, ​null​, ​1​)​}​`​;
            } ​else​ {
                ​return​ ​`​${​info​.​timestamp​}​ [​${​info​.​label​}​] ​${​info​.​level​}​: ​${​info​.​message​}​ Stack: ​${​info​.​stack​}​`​;
            }
        } ​else​ {
            ​return​ ​`​${​info​.​timestamp​}​ [​${​info​.​label​}​] ​${​info​.​level​}​: ​${​info​.​message​}​`​;
        }
    })
);

Error: Module did not self-register. (For onoff package require)

I am trying to require the package "onoff" in js file on one of he node js project. When i run a js file i get error as below
\node_modules\bindings\bindings.js:88
throw e
^
Error: Module did not self-register.
at Object.Module._extensions..node (module.js:670:18)
at Module.load (module.js:560:32)
at tryModuleLoad (module.js:503:12)
at Function.Module._load (module.js:495:3)
at Module.require (module.js:585:17)
at require (internal/module.js:11:18)
at bindings (\node_modules\bindings\bindings.js:81:44)
at Object.<anonymous> (\node_modules\epoll\epoll.js:1:99)
at Module._compile (module.js:641:30)
at Object.Module._extensions..js (module.js:652:10)
Please help through this.
Thanks in advance
Pallavi K
I've run into this issue as well and ended up mocking the library for local development. There has been a few issues created over the years, and it seems like the author either doesn't have OSX to test or he just isn't interested in supporting OSX in general.
Issues created related to this problem:
https://github.com/fivdi/epoll/issues/12
https://github.com/fivdi/onoff/issues/69
https://github.com/fivdi/onoff/issues/106
This is the work around I have:
// GpioFactory.js
class MockGPIO {
constructor(pin, direction) {
this._value = 0;
this._direction = direction;
}
readSync() { return this._value; }
read(cb) { cb(null, this._value) }
writeSync(value) { this._value = value }
write(value, cb) {
this._value = value;
cb(null, value);
}
watch(cb) {}
unwatch(cb) {}
unwatchAll() {}
direction() { return this._direction }
setDirection(direction) { this._direction = direction}
edge() { return 0; }
setEdge(edge) {}
activeLow() { return true; }
setActiveLow(invert) {}
unexport() {}
}
MockGPIO.accessible = false;
MockGPIO.HIGH = 1;
MockGPIO.LOW = 0;
module.exports = {
create: () => {
try {
return require('onoff').Gpio;
} catch (e) {
console.error('Using mock Gpio');
return MockGPIO;
}
}
};
The actual fix is the create() method that just returns the mock class. This allows my client code to use both the same way:
const GpioFactory = require('./GpioFactory');
const Gpio = GpioFactory.create();
const garageButton = new Gpio(4, 'out');
I don't use the full API of the library, so this example is likely missing some details.
Update: 12/15/2018
I submitted a PR to allow the accessible property to work on OSX as described in the docs. Hopefully it'll get merged.
PR: https://github.com/fivdi/onoff/pull/122

Cannot find module '../dialog' (Electron fatal error)

In electron, I encounter the following error:
module.js:440
throw err;
^
Error: Cannot find module '../dialog'
at Module._resolveFilename (module.js:438:15)
at Function.Module._resolveFilename (/opt/App/resources/electron.asar/common/reset-search-paths.js:47:12)
at Function.Module._load (module.js:386:25)
at Module.require (module.js:466:17)
at require (internal/module.js:20:19)
at Object.get [as dialog] (/opt/App/resources/electron.asar/browser/api/exports/electron.js:35:14)
at process.<anonymous> (/opt/App/resources/electron.asar/browser/init.js:64:31)
at emitOne (events.js:96:13)
at process.emit (events.js:188:7)
at process._fatalException (node.js:276:26)
It happens on a child process spawn that fails in Linux. Strange because I do have a try catch block around that, yet it still results in an uncaughtexception, as seen in the code in browser/init.js from electron.asar:
// Don't quit on fatal error.
process.on('uncaughtException', function (error) {
// Do nothing if the user has a custom uncaught exception handler.
var dialog, message, ref, stack
if (process.listeners('uncaughtException').length > 1) {
return
}
// Show error in GUI.
dialog = require('electron').dialog
stack = (ref = error.stack) != null ? ref : error.name + ': ' + error.message
message = 'Uncaught Exception:\n' + stack
dialog.showErrorBox('A JavaScript error occurred in the main process', message)
}
As said, my code is in a try catch:
try {
server = childProcess.spawn(java, ["-jar", "App.jar"], {
"cwd": serverDirectory,
"detached": true
}, function(err) {
console.log("in callback");
});
} catch (err) {
console.log("here we are");
console.log(err);
}
But neither the callback nor the catch block is reached. Any ideas what is going on here and why the default dialog module cannot be found?
I found same error with electron 1.6.2
Figured it was due, when closing the application an error occur and electron want to display it in a dialog, maybe the close process has started and electron can't load this module, anyway I add:
const { dialog } = require('electron');
in main.js, no more error in console instead a dialog the error, I can fix it, After that I let the require just in case.
I hope I understand your question correctly...
If by "default dialog module", you mean the electron dialog API, then you can require that like so: const { dialog } = require('electron'). (Or in pre-1.0 versions simply require('dialog'). See https://github.com/electron/electron/blob/master/docs/api/dialog.md
Also the try/catch needs to be around the require in the child process. The try/catch you have is around the spawning of the child process in the parent. That require is failing in an entirely different node.js process, so it's not caught in the parent process that spawned it. It sounds like your child process code might work better if it looked like:
try {
const dialog = require('dialog');
} catch(e) {}
Also, if childProcess.spawn is referring to the core node module child_process, that doesn't accept a callback function. See https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
Can you share the code from your child process? That might help more.

Resources