{"record":{"id":"f9148680642c2705","repo":"koajs/koa","slug":"non-error-thrown-j","errorCode":null,"errorMessage":"non-error thrown: %j","messagePattern":"non-error thrown: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/application.js","lineNumber":260,"sourceCode":"    context.state = {}\n    return context\n  }\n\n  /**\n   * Default error handler.\n   *\n   * @param {Error} err\n   * @api private\n   */\n\n  onerror (err) {\n    // When dealing with cross-globals a normal `instanceof` check doesn't work properly.\n    // See https://github.com/koajs/koa/issues/1466\n    // We can probably remove it once jest fixes https://github.com/facebook/jest/issues/2549.\n    const isNativeError =\n      Object.prototype.toString.call(err) === '[object Error]' ||\n      err instanceof Error\n    if (!isNativeError) { throw new TypeError(util.format('non-error thrown: %j', err)) }\n\n    if (err.status === 404 || err.expose) return\n    if (this.silent) return\n\n    const msg = err.stack || err.toString()\n    console.error(`\\n${msg.replace(/^/gm, '  ')}\\n`)\n  }\n\n  /**\n   * Help TS users comply to CommonJS, ESM, bundler mismatch.\n   * @see https://github.com/koajs/koa/issues/1513\n   */\n\n  static get default () {\n    return Application\n  }\n}\n","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/koajs/koa/blob/571938d1b42d5bbfbc4f203202e1095a63003f7e/lib/application.js#L242-L278","documentation":"Re-thrown by app.onerror() when middleware, a promise rejection, or a stream error delivers a value that is not a native Error. Koa's error contract requires real Error instances; the handler does a cross-global-safe check (Object.prototype.toString === '[object Error]' || instanceof Error) to cover jest/vm realms, and when that fails it formats the offending value via util.format('%j') and throws a TypeError so the bad value is never silently swallowed. It surfaces from handleRequest's .catch(onerror) chain, meaning anything a middleware throws or rejects with is funneled here.","triggerScenarios":"Any middleware doing throw 'not found', throw 404, throw { status: 400, message: 'bad' }, or return Promise.reject('nope'). A third-party dependency that rejects with a string or plain object. Code that throws the value of an env var or a parsed JSON number. ctx.throw() itself is safe, but a custom helper that does throw res.statusCode (a number) triggers it. A stream piped to the response emitting a non-Error 'error' event.","commonSituations":"Porting Express-style throw 'bad request' patterns to Koa. Rejecting promises with status codes (reject(401)) in auth helpers. Older Node patterns of throw 'message'. Libraries upgraded to reject with custom error-like objects that fail the cross-global check under jest/jsdom. Assertion libraries that throw non-Error values in certain modes.","solutions":["Find the throw/reject site: search the codebase for throw ' (throw of string literals) and reject( that is not passed a new Error().","Replace string/number/object throws with Error instances: throw new Error('not found') or better throw Object.assign(new Error('not found'), { status: 404 }).","Use Koa's built-in ctx.throw(status, msg) which constructs a proper HttpError with status and expose flags.","If a dependency rejects with non-Errors, wrap the call: try { await lib(x) } catch (e) { throw e instanceof Error ? e : new Error(String(e)) }.","Add a global normalizing middleware last in the stack that catches ctx errors and re-throws as Error so the app.onerror guard never trips."],"exampleFix":"// before\nasync function auth(ctx, next) {\n  if (!ctx.headers.authorization) throw 'unauthorized'   // string, not an Error\n  await next()\n}\n\n// after\nasync function auth(ctx, next) {\n  if (!ctx.headers.authorization) ctx.throw(401, 'unauthorized')  // builds a native HttpError\n  await next()\n}","handlingStrategy":"try-catch","validationCode":"// normalize anything you are about to throw/reject so app.onerror never sees a non-Error\nfunction toError(value) {\n  if (value instanceof Error) return value\n  if (typeof value === 'object' && value !== null && typeof value.message === 'string') {\n    return Object.assign(new Error(value.message), value)\n  }\n  return new Error(String(value))\n}\n\n// before rejecting\nif (!ok) throw toError(reason)","typeGuard":"// cross-global-safe native Error check matching Koa's own guard\nfunction isNativeError(err) {\n  return (\n    Object.prototype.toString.call(err) === '[object Error]' ||\n    err instanceof Error\n  )\n}\n\nif (!isNativeError(thrownValue)) {\n  // re-wrap before it reaches app.onerror\n  throw Object.assign(new Error(String(thrownValue)), { original: thrownValue })\n}","tryCatchPattern":"// terminal normalizing middleware: register LAST so app.onerror only ever sees native Errors\napp.use(async (ctx, next) => {\n  try {\n    await next()\n  } catch (err) {\n    if (Object.prototype.toString.call(err) === '[object Error]' || err instanceof Error) {\n      throw err // already compliant\n    }\n    // wrap strings, numbers, plain objects\n    const wrapped = new Error(typeof err === 'string' ? err : JSON.stringify(err))\n    wrapped.status = (err && err.status) || 500\n    wrapped.expose = false\n    throw wrapped\n  }\n})","preventionTips":["Never throw primitives: ban throw '<string>' and throw <number> via lint rules (e.g. eslint no-throw-literal with throwAny: false where supported).","Use ctx.throw(status, message) for HTTP errors so Koa always constructs a native HttpError for you.","Reject promises only with new Error(...) or a subclass; reject('msg') will reach onerror as a non-Error.","When calling third-party libraries that may reject with non-Errors, wrap them in try/catch and re-throw via toError().","Register a terminal normalizing catch middleware so a stray non-Error from any dependency never crashes app.onerror."],"tags":["error-handling","async","middleware","type-error"],"backgroundTag":null,"analyzedSha":"571938d1b42d5bbfbc4f203202e1095a63003f7e","analyzedAt":"2026-08-04T13:07:31.940Z","contentChangedAt":"2026-08-04T13:07:31.940Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}