A forgotten await lets code continue before an operation finishes, and a rejection nobody handles can crash the process. In Node.js the default for an unhandled rejection is to raise it as an uncaught exception. The typescript-eslint rule no-floating-promises finds these statements before they ship.
The security shape of the bug
Consider an authorization helper that returns a promise. If a route calls it without await, the handler carries on and serves the data while the check runs in the background. If the check later rejects, nothing stops the response that already went out. The tests pass, because the happy path still works.
The same pattern appears with audit log writes, token revocation and cache invalidation. In each, the code looks right and does nothing useful.
What Node does with an unhandled rejection
The process docs say the unhandledRejection event fires when a promise is rejected and no handler is attached within a turn of the event loop. If that event is emitted but nobody handles it, it is raised as an uncaught exception. The CLI docs name throw as the default mode for --unhandled-rejections. An uncaught exception prints a stack trace and exits with code 1 unless you added a handler. See the modes compared for the other options.
What the lint rule checks
The typescript-eslint docs define a floating promise as one created without any code to handle errors it might throw. The rule treats a promise as handled when you:
- call
.then()with two arguments, - call
.catch()with one argument, - use
await, or - use
return.
It also accepts the void operator by default through ignoreVoid, but the docs warn that voiding does not handle the promise, so its rejection is still unhandled. It flags arrays of promises too and suggests Promise.all, Promise.allSettled, Promise.any or Promise.race. It needs type information and is part of recommended-type-checked.
Steps to adopt it
- Enable typed linting using the preset guide.
- Run the rule and read each finding. Most are real.
- Fix with
awaitwhere order matters, orreturnthe promise. - For deliberate fire and forget work, add a
.catch()that logs, instead ofvoidalone. - Enable
no-misused-promisesas well. The docs say misuse in places such asifconditions is covered by that separate rule, not this one. - Make lint a required check.
Things the rule cannot see
It will not know about a function that returns a promise through an untyped any, which is another reason to keep noImplicitAny on. It also cannot tell you the right behavior on failure, such as deny on error for authorization.
Quick check
Does void make a promise safe?
No. The rule allows it by default, but the docs say a voided promise's rejection remains unhandled.
Does the rule work without type information?
No. It requires type information and is enabled by recommended-type-checked.
Sources
Last reviewed: 19 October 2026.