← OWASP Top 10:2025

A10:2025

Mishandling of Exceptional Conditions

What the app does when something goes wrong: fails open, leaks a stack trace, catches and ignores, or crashes on input it did not expect.

What it is

This is the newest category. It covers how code behaves off the happy path. An auth check that throws and is caught by a handler that continues as if it passed. An empty catch block that hides a failure. An error page that prints the stack trace, the query and the file path. A regular expression that hangs on a crafted string. A parser that crashes the process on malformed input. Each is a place where the exception, not the attacker, opens the door.

OWASP added it because these failures showed up across many tested applications and did not fit anywhere else, and because the fail-open pattern in particular turns a harmless bug into a bypass.

How it shows up in AI-generated code

AI coding tools write a lot of try/catch, and the catch is very often either empty, a console.log, or a return of a default value that lets execution continue. It makes the code look robust. In a permission check it means an exception equals allowed. In a payment handler it means a failed verification equals success. The tool is optimising for the app not crashing, which is not the same as the app being safe.

The other AI-specific patterns: stack traces returned in API error responses because the tutorial did res.status(500).json({ error: err.message, stack: err.stack }); and catastrophic-backtracking regular expressions generated for validation, which let a single request pin a CPU. Prbl flags the leaked stack trace and the ReDoS patterns; the fail-open cases need a human reading the catch blocks.

Example: A permission check that fails open

The pattern
let allowed = true;
try {
  allowed = await can(user, "delete", doc);
} catch (e) {
  console.log(e);            // permission service down: still allowed
}
if (allowed) await deleteDoc(doc);
The fix
let allowed = false;                        // default deny
try {
  allowed = await can(user, "delete", doc);
} catch (e) {
  log.error({ event: "authz_error", err: e });
  return new Response("Service unavailable", { status: 503 });   // fail closed
}
if (!allowed) return new Response("Forbidden", { status: 403 });
await deleteDoc(doc);

The default value and the catch branch decide the security of the whole function. Default deny, and on error stop rather than continue.

How to find it

  • Read every catch block near auth, payment and data-changing code. If it logs and continues, or is empty, that is a fail-open candidate.
  • Trigger an error on an API route and look at the response body for a stack trace, file path or query.
  • Search for regular expressions with nested quantifiers applied to user input.
  • Send malformed input (huge numbers, empty strings, wrong types) to endpoints and watch for crashes rather than 400s.
  • Prbl covers the leaked stack trace and ReDoS rules; the fix guides cover the patterns.

How to fix it

  • Default deny. Initialise security decisions to false and only set true on an explicit pass.
  • Fail closed on error in any security-relevant path: return an error, do not continue.
  • Never send err.stack or err.message from a framework to a client. Log it, return a generic message and a request ID.
  • Validate input with a schema at the boundary so the rest of the code sees expected shapes.
  • Avoid regular expressions with nested quantifiers on untrusted input; use a linear-time matcher or a length cap.

What Prbl checks for this category

Stack trace leaked to clientReDoS catastrophic backtrackingDebug mode in production

Run a free scan on a public repo or a live URL. Findings link to the fix guides below.

Fix guides for this category

Go deeper

Common questions

What does fail open mean?

When a check cannot complete, the code proceeds as if it passed. A permission service timing out and the request going through anyway is failing open. The safe behaviour is to fail closed: treat an error as a denial and stop.

Is an empty catch block a vulnerability?

By itself it is a bug. Near authentication, authorization, payment verification or input validation it is a vulnerability, because the error it hides is the thing that would have stopped the request. Read those catch blocks specifically.

Why is this new in 2025?

OWASP's 2025 data collection found error handling and exceptional-path failures widespread enough, and distinct enough from misconfiguration and injection, to earn a category. The rise of generated code that wraps everything in try/catch is part of the picture.

Is this category already in something you shipped? Scan a live URL or a public repo free, no account.

Scan my app →
A10:2025 Mishandling of Exceptional Conditions Explained, With AI-Generated Code Examples | Prbl