You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[fix] ctx.assert() throws specific error classes #1925
Reason
This issue involves the problem of cross-module instance checks with instanceof in JavaScript and the complexity of Koa's dependency chain.
Dependency chain:
Koa
├── ctx.throw() → Use http-errors directly
├── ctx.assert() → Use http-assert → Indirectly use http-errors
└── Export HttpError → From http-errors
Question:
http-errors creates specific error subclasses: When calling createError(401, 'Unauthorized'), what is actually returned is an instance of the UnauthorizedError class, not the HttpError class itself.
Multiple instance issue: If http-assert and koa each depend on different versions of http-errors, then their HttpError base classes are actually different constructor instances, causing instanceof checks to fail.
npm dependency resolution: In npm/yarn/pnpm, if the version ranges are different, multiple copies of http-errors may be installed:
node_modules/
├── http-errors/ # The version of koa's dependency
└── http-assert/
└── node_modules/
└── http-errors/ # Different versions of http-assert dependencies
Verification:
import{HttpError}from'koa'importcreateErrorfrom'http-errors'// ctx.throw() The behavior of...constthrowError=createError(401,'Unauthorized')console.log(throwErrorinstanceofHttpError)// ✅ true (from the same http-errors)console.log(throwErrorinstanceofcreateError.HttpError)// ✅ trueconsole.log(throwError.constructor.name)// 'UnauthorizedError'// The behavior of ctx.assert() - different versions of http-errors may be used// If the versions are inconsistent, the instanceof check will fail
Solution:
Use createError.isHttpError() for checking (recommended)
The http-errors module provides the isHttpError() function, which uses duck typing and does not rely on instanceof:
importcreateErrorfrom'http-errors'app.use(async(ctx,next)=>{try{awaitnext()}catch(err){// ✅ Recommendation: Use isHttpError for the check.if(createError.isHttpError(err)){ctx.status=err.statusctx.body={message: err.message}}else{ctx.status=500ctx.body={message: 'Internal Server Error'}ctx.app.emit('error',err,ctx)}}})
The internal implementation of isHttpError() is based on duck typing:
// The implementation logic of the isHttpError method in http-errorsfunctionisHttpError(val){if(!val||typeofval!=='object')returnfalse// Check if it is an instance of HttpErrorif(valinstanceofHttpError)returntrue// Duck type checkingreturntypeofval.status==='number'&&typeofval.statusCode==='number'&&typeofval.expose==='boolean'}
Activity
isjs-d commentedon Jan 25, 2026
Update: This is likely an issue with http-assert, which uses: "http-errors": "~1.8.0"
while Koa uses: "http-errors": "^2.0.0"
Jerry-CodeHub commentedon Jan 29, 2026
Reason
This issue involves the problem of cross-module instance checks with
instanceofin JavaScript and the complexity of Koa's dependency chain.Dependency chain:
Koa
├── ctx.throw() → Use http-errors directly
├── ctx.assert() → Use http-assert → Indirectly use http-errors
└── Export HttpError → From http-errors
Question:
http-errorscreates specific error subclasses: When callingcreateError(401, 'Unauthorized'), what is actually returned is an instance of theUnauthorizedErrorclass, not theHttpErrorclass itself.http-assertandkoaeach depend on different versions ofhttp-errors, then theirHttpErrorbase classes are actually different constructor instances, causinginstanceofchecks to fail.http-errorsmay be installed:node_modules/
├── http-errors/ # The version of koa's dependency
└── http-assert/
└── node_modules/
└── http-errors/ # Different versions of http-assert dependencies
Verification:
Solution:
Use createError.isHttpError() for checking (recommended)
The http-errors module provides the isHttpError() function, which uses duck typing and does not rely on instanceof:
The internal implementation of isHttpError() is based on duck typing:
fix: ctx.assert() now throws errors instanceof HttpError
fix: ctx.assert() throws HttpError instances
patch: make ctx.assert errors instanceof HttpError (koajs#1998)