Hello I am trying to send an error from the beforeCreate callback in my model like this:
next([ { httpCode: 400,
code: 997,
message: 'username already exists',
context: 'user create',
custom: true } ]);
and I get the following error
error: Error (E_UNKNOWN) :: Encountered an unexpected error
at new WLError (/home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/lib/waterline/error/WLError.js:33:18)
at duckType (/home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/lib/waterline/error/index.js:67:10)
at errorify (/home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/lib/waterline/error/index.js:39:10)
at wrappedCallback (/home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/lib/waterline/utils/normalize.js:320:15)
at _normalizeCallback.callback.error (/home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/node_modules/node-switchback/lib/normalize.js:42:31)
at _switch (/home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/node_modules/node-switchback/lib/factory.js:33:28)
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/lib/waterline/query/dql/create.js:59:22
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/node_modules/async/lib/async.js:254:17
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/node_modules/async/lib/async.js:151:21
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/node_modules/async/lib/async.js:251:21
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/node_modules/async/lib/async.js:615:34
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/waterline/node_modules/async/lib/async.js:151:21
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/api/models/User.js:153:15
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/async/lib/async.js:533:17
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/async/lib/async.js:119:25
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/async/lib/async.js:24:16
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/node_modules/sails/node_modules/async/lib/async.js:530:21
at /home/angelkyriako/Projects/vc/Code/backend/virtualclass/api/utilities/Utils.js:74:12
Details: [ { httpCode: 400,
code: 997,
message: 'username already exists',
context: 'user create',
custom: true } ]
After some expiriments I found that this:
Details: [ { httpCode: 400,
code: 997,
message: 'username already exists',
context: 'user create',
custom: true } ]
is a string contained in:
err.details
Although, I cannot parse it with JSON.parse and use it for my error response.
Any thoughts on how to prevent sails with throwing this error ?
I have figured out a dummy workaround that works only if I call the callback with JSON.stringify like this:
next(JSON.stringify([ { httpCode: 400,
code: 997,
message: 'username already exists',
context: 'user create',
custom: true } ]));
and I handle the error like this:
if (err.code === 'E_UNKNOWN')
try {
err = JSON.parse(err.details.last(err.details.length - 'Details: '.length))
} catch (e) {}
Before this, I also tried to use the new Error() contructor in case that was what it needed but with no luck.
Please let me know if there is any way to prevent this E_UNKNOWN error.
Waterline wraps the Errors: https://github.com/balderdashy/waterline/blob/master/lib/waterline/error/index.js#L81
I really dont understand this wired behaviour but returning something like
var err = {
code: 'E_UNIQUE',
details: 'Invalid',
model: 'cronjob',
invalidAttributes: {
hase: []
},
status: 400
}
return cb(err);
gives me the correct 400
I will look into it a bit more in case I can provide any more feedback to you.
For now something else I have noticed is that this happens only for the beforeCreate callback.
I will check with the same error as the one that you tested with and get back to you with info for each callback because I doubt that I have used every lifecycle cb.
ciao for now
Sails throws an E_UNKNOWN or E_USAGE error for:
Sails is trying to create an WLValidationError by using my custom error object as a parameter for the WLValidationError constructor. If the properties of the object are not the expected ones it obviously complains.
this happens when the object that is passed to the next callback does not contain the properties below:
{ code: 'E_UNIQUE' [missing it causes E_UNKNOWN] invalidAttributes: whatever [missing it causes E_USAGE] }
(this is the reason your example works).
I do not know what sails is supposed to be doing in this case, from your end and therefore I am not messing with trying to fix this issue. I worked around it by setting the code and invalidAttributes properties on every error object of my server as above.
If this is the intended behavor I believe this should be added to the documentation under this section:
http://sailsjs.org/#/documentation/concepts/ORM/Lifecyclecallbacks.html
As far as my case consider this issue a closed one.
Thank you for your answer,
Angel
I proud to here what the sails-team say about this behaviour
I also have same issue in beforeCreate, tried to send error after validation failed in the method. Is there a official way to handle these error case in sails js? otherwise, I might need to follow the workaround that @AngelKyriako mentioned perviously.
Thanks for posting, @AngelKyriako. I'm a repo bot-- nice to meet you!
It has been 30 days since there have been any updates or new comments on this page. If this issue has been resolved, feel free to disregard the rest of this message. On the other hand, if you are still waiting on a patch, please:
Thanks so much for your help!
A lot of workarounds for what. I got to this cause you cannot use nested data into models and i need to validate everything. Can someone put something that actually work here? I tried Angels version, but that try got me to catch. I really like sails, but this waterline can be removed.
Now i try to parse Details : {message: "something", code:400} that got me to unexpected token D because that's not a json
@AleRob90 I do not have an update for that on the system I develop, but we are far from the current version of sails.js (we are still at v0.10.5). I was wondering what version is the sails framework on your end ?
Furthermore, sails.js lets you use native queries for each database driver.
That means that:
1). Their write db methods do not pass through the lifecycle callbacks
2). attribute translation does not occur(.eg for mongo u will have to query for _id instead of id)
3). updatedAt/createdAt is not auto generated
Bellow is an example of how I use native methods with the mongo driver when I do not need waterline features.
services/DB.js:
native: function(model, opts){
if (!opts) opts = {};
// do stuff with opts
return {
create: function (createObj, mongoOpts, next) {
if (Object.isFunction(mongoOpts)) {
next = mongoOpts;
mongoOpts = {};
}
model.native(function(err, native) {
if (err) next(err);
else {
createObj.active = true;
native.insert(createObj, mongoOpts, next);
}
});
},
find: function(query, mongoOpts, next) {
if (Object.isFunction(mongoOpts)) {
next = mongoOpts;
mongoOpts = {};
}
model.native(function(err, native) {
if (err) next(err);
else native.find(query, mongoOpts).toArray(next);
});
},
update: function (query, updateObj, mongoOpts, next) {
if (Object.isFunction(mongoOpts)) {
next = mongoOpts;
mongoOpts = {};
}
model.native(function(err, native) {
if (err) next(err);
else {
native.update(query, updateObj, mongoOpts, next);
}
});
}
};
}
other.js
and I use it like this
DB.native(User, {random: 'option'}).create({name: 'tsubaka'}, function(err, usr){
if (usr) {
console.log(usr);
DB.native(User, {name: 'tsubaka'}, function(err, tsuby) {
if (tsuby)
console.log(tsuby)
});
}
})
suppose your lifecycle callback is
beforeValidate: function(values,cb){
cb(JSON.stringify({ httpCode: 400,
code: 997,
message: 'username already exists',
context: 'user create',
custom: true }));
}
try to stringify the error object that you are sending
In the controller you can recieve this error as string in the format
Details : {error object defined by you}
Now in the controller
// this error object is recieved inside the callback or catch of promise
if(error.hasOwnProperty('details')){
var indexStart=error.details.indexOf('{');
//this is where we are extracting the error code that we set inside lifecycle callback
var data =error.details.substring(indexStart);
res.json(500,JSON.parse(data));
}else{
res.json(500,{message : "your message",status : 'your status',data : error});
}
I know it's a old post, but I have found another solution that could be helpful.
//in api/models/User.js
function validationError(invalidAttributes, status, message) {
var WLValidationError = require('../../node_modules/sails/node_modules/waterline/lib/waterline/error/WLValidationError.js');
return new WLValidationError({
invalidAttributes: invalidAttributes,
status: status,
message: message
}
);
}
var User = {
attributes: {
//...
},
ownValidate:: function (values, update, cb) {
//example of not allowed param on update
//if it is an update then do not allow email param
if (update && values.email) {
return cb(validationError({
email: [
{
message: 'Email is not allowed for updates.'
}
]
}, 400 /*status*/));
}
sails.models['user'].findOne(values.email).exec(function (err, user) {
if (err) return cb(err);
if (user) {
return cb(validationError({
email: [
{
value: values.email,
rule: 'E_UNIQUE'
/* unique validation message is left for the default one here */
}
]
}, 409));
}
});
},
beforeCreate: function (values, cb) {
return sails.models['user'].ownValidate(values, false, cb);
},
beforeUpdate: function (values, cb) {
return sails.models['user'].ownValidate(values, true, cb);
}
}
For blueprint custom messages validation
// For adding custom messages if using blueprint
// In reponses/badrequest.js
// after
// if (sails.config.environment === 'production') {
// data = undefined;
// }
// add
if (data.invalidAttributes !== undefined) {
var attributes = data.invalidAttributes;
_.each(attributes, function (attribute, key) {
_.each(attribute, function (properties) {
var rule = properties.rule;
var message = false;
try {
message = sails.__('validation.' + key + '.' + rule, key);
}
catch (e) {
}
try {
message = sails.__('validation.' + rule, key);
}
catch (e) {
}
if (message && message != 'validation.' + rule && message != 'validation.' + key + '.' + rule) {
properties.message = message;
}
});
});
}
// In config/i18n.js
//add
objectNotation: true
// example :
//In config/locales/en.json or your locale file
//add
"validation": {
"isJson": "invalid JSON format in %s"
}
Thanks to GadElKareem