I stumbled upon this issue when trying to throw an Unauthorized error like:
...,
async (ctx, next) => {
if (!ctx.state.user) {
ctx.throw(401, 'Unauthorized');
}
ctx.state.payload.user = ctx.state.user;
return await next();
}
...
The response’ status code always was 404. According to Koa docs the use of ctx.throw above is equivalent to:
...,
async (ctx, next) => {
if (!ctx.state.user) {
ctx.status = 401;
throw new Error('Unauthorized');
}
ctx.state.payload.user = ctx.state.user;
return await next();
}
...
But it isn’t. Setting status first and then throwing an error results in a correct 401 Unauthorized response as expected while using ctx.throw doesn’t.
Looking at the relevant code there is just an http-error created and thrown but nothing indicates that ctx.status is being set.
throw() {
throw createError.apply(null, arguments);
},
So it seems the response comes with status overridden by Koa’s default 404, no matter what status code you pass to ctx.throw.
try return ctx.throw(401, 'Unauthorized') ??
Tried already. createError – imported from the third-party http-errors module – is passed the arguments array of context’s .throw method and itself doesn’t care in which order the arguments are passed, as it extracts the values depending on their type.
Found the cause of my problem already. As soon as you catch the error in one of the middleware functions (as I do up the stream), onerror is called with null and doesn’t go ahead with default error handling. So I have to assign the error’s status code to ctx.status myself. My bad! Thanks anyways. Closing.
@oliverwehn mean is we need handle the err in middleware?
@abnerCrack: It means if you want to handle errors (or particular error types) your way instead of relying on Koa’s own basic error handling and responses, you should build middleware taking care of this. Use it up-stream to catch errors down the stream like:
const router = new Router();
router.use(
function (ctx, next) {
try {
// Go down the stream
return await next();
} catch (err) {
// If an error occurs down the stream and no response is sent by
// another middleware before, the error gets caught up here
const response = {};
// Set status on ctx
ctx.status = parseInt(error.status, 10) || ctx.status || 500;
// Build response or do whatever you want depending on status
switch (ctx.status) {
case 400:
case 401:
case 403:
case 404:
case 500: {
response.error = { message: err.message };
break;
}
default: {
response.error = { message: 'Unknown error' };
}
}
// End processing by sending response
ctx.body = response;
}
},
middleware1(),
middleware2()
);
module.exports = router;
@oliverwehn thanks a lot. I think I met the same problem i will try it
The above code: 403 is duplicated, error is not defined, but thanks for the sample
@reduxdj: Yeah, you’re right. Fixed the code sample. Thanks.
@oliverwehn do you mean that when we want to throw an error, we should both set the ctx.status to 422 and then ctx.throw(422)? That's how it worked for me but I dont feel good on this behaviour:
async function validate_signup_request(ctx: Koa.Context, next: Function) {
var result = validate(ctx.request.body);
if(!result){
ctx.status=422;
ctx.throw(422, "Invalid signup request");
}
else{
await next();
}
}
If I dont set the ctx.status to 422, the error handler catches the error but the ctx.state is somehow 404
Good to know, thanks.
The problem remains. Thank you for your code example
Most helpful comment
try return ctx.throw(401, 'Unauthorized') ??