{"record":{"id":"19569d680385b6f1","repo":"oxc-project/oxc","slug":"express-endpoint-handler-for-endpoint-should-n","errorCode":null,"errorMessage":"Express endpoint handler for `{endpoint}` should not be `async`.","messagePattern":"Express endpoint handler for `(.+?)` should not be `async`\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"error","filePath":"crates/oxc_linter/src/rules/oxc/no_async_endpoint_handlers.rs","lineNumber":78,"sourceCode":"        // Shouldn't happen, since separate declaration/registration requires an\n        // identifier to be bound\n        (Some(span), None) => &[\n            async_span.label(\"Async handler is declared here\"),\n            span.primary_label(registered_label),\n        ],\n        // `app.get('/foo', async function foo(req, res) {});`\n        (None, Some(name)) => &[async_span.label(format!(\"Async handler '{name}' is used here\"))],\n\n        // `app.get('/foo', async (req, res) => {});`\n        (None, None) => &[async_span.label(\"Async handler is used here\")],\n    };\n\n    let warning = endpoint.map_or_else(\n        || \"Express endpoint handler should not be `async`.\".to_string(),\n        |endpoint| format!(\"Express endpoint handler for `{endpoint}` should not be `async`.\"),\n    );\n\n    OxcDiagnostic::warn(warning)\n        .with_labels(labels.iter().cloned())\n        .with_help(\n            \"Wrap the async handler and forward errors to `next()` (e.g. `(req, res, next) => Promise.resolve(handler(req, res, next)).catch(next)`).\\nExpress does not automatically handle rejected promises from async handlers, which results in unhandled promise rejections and server crashes.\",\n        ).with_note(\n            \"If you're on Express 5, disable this rule. To allow specific functions, add their names to `allowedNames`.\"\n        )\n}\n\ndeclare_oxc_lint!(\n    /// ### What it does\n    ///\n    /// Disallows the use of `async` functions as Express endpoint handlers.\n    ///\n    /// ### Why is this bad?\n    ///\n    /// Before v5, Express will not automatically handle Promise rejections from\n    /// handler functions with your application's error handler. You must\n    /// instead explicitly pass the rejected promise to `next()`.","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/oxc/no_async_endpoint_handlers.rs#L60-L96","documentation":"Oxlint rule `oxc/no_async_endpoint_handlers` flags `async` functions passed as Express route handlers. Express 4 does not catch promise rejections from handlers: a rejected async handler becomes an unhandled promise rejection, skipping the error middleware (`next(err)` is never called) and potentially crashing Node on `unhandledRejection`. The message interpolates the endpoint path (or the generic form when the path is not a static string), and the note says to disable the rule on Express 5, which handles rejections natively.","triggerScenarios":"`app.get('/foo', async (req, res) => { ... })`, `router.post('/bar', async function handler(req, res) { ... })`, or the same as middleware-array entries — i.e. any async function value passed where Express expects a handler, with the route path known at lint time. Config `allowedNames` exempts specifically named functions.","commonSituations":"Express 4 apps with async/await DB or fetch calls inside handlers where errors vanish past the error middleware; Node 15+ processes exiting on unhandled rejections; teams upgrading to Express 5 but keeping the rule enabled.","solutions":["Wrap handlers so rejections reach `next`: `const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next)` and register `wrap(asyncHandler)`","Or use a library: `express-async-handler` or the `express-async-errors` shim imported once at startup","Use try/catch inside the handler and call `next(err)` in the catch block","Add the handler's name to the `allowedNames` option for deliberate exceptions","On Express 5, disable the rule: `\"oxc/no_async_endpoint_handlers\": \"off\"`"],"exampleFix":"// before\napp.get('/users', async (req, res) => {\n  const users = await db.all('SELECT * FROM users');\n  res.json(users);\n});\n\n// after\nconst wrap = (fn) => (req, res, next) =>\n  Promise.resolve(fn(req, res, next)).catch(next);\n\napp.get('/users', wrap(async (req, res) => {\n  const users = await db.all('SELECT * FROM users');\n  res.json(users);\n}));","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"const wrap = (fn) => (req, res, next) =>\n  Promise.resolve(fn(req, res, next)).catch(next);\n\napp.get('/users', wrap(async (req, res) => {\n  res.json(await db.all());\n}));\n// all handler rejections are forwarded to the Express error middleware","preventionTips":["Adopt one wrapper convention (express-async-handler or a shared wrap util) project-wide and ban raw async handlers in code review","Add a process-level unhandledRejection logger so any unwrapped handler is visible in production","On Express 5, remove the wrapper convention and disable the rule to avoid double-handling confusion"],"tags":["express","async","unhandled-rejection","oxlint","error-handling","server"],"backgroundTag":"express-async-handler-unhandled-rejection","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}