Weakness reference
CWE-391

Unchecked Error Condition

This weakness occurs when code calls a function or performs an operation but fails to check whether it succeeded or failed. When errors go undetected, the…

01Summary

This weakness occurs when code calls a function or performs an operation but fails to check whether it succeeded or failed. When errors go undetected, the program may continue with invalid data, corrupted state, or incomplete results — leading to crashes, data loss, or security vulnerabilities. Proper error handling is a foundational defensive practice.

02How It Happens

Most operations — file I/O, network calls, database queries, memory allocation, authentication checks — can fail for legitimate reasons (file not found, network timeout, permission denied, out of memory). When a developer omits the check for success or failure, the code assumes the operation worked and proceeds with whatever state resulted. This can mean using a null pointer, processing incomplete data, or trusting an operation that silently failed. Over time, unchecked errors accumulate and create conditions where the application behaves unpredictably or unsafely.

03Real-World Impact

Unchecked errors can cause application crashes, data corruption, or silent failures that go unnoticed until they cause downstream harm. In security contexts, an unchecked authentication or validation error might allow an attacker to bypass a check entirely. File operations that fail silently can leave the application in an inconsistent state. Database transactions that fail without detection can result in partial updates. The impact ranges from availability issues (denial of service through crashes) to integrity issues (corrupted or incomplete data) to confidentiality issues (if error conditions expose sensitive information in logs or error messages).

04Vulnerable & Fixed Patterns

Vulnerable pattern
import json

def load_user_config(filename):
    with open(filename, 'r') as f:
        config = json.load(f)
    
    user_id = config['user_id']
    return user_id

Why it's vulnerable:
The code does not check whether the file exists, whether json.load() succeeds, or whether the 'user_id' key exists in the parsed JSON. Any of these can fail silently or raise an unhandled exception, leaving the caller with no way to know the operation failed.

Fixed pattern
import json

def load_user_config(filename):
    try:
        with open(filename, 'r') as f:
            config = json.load(f)
    except (FileNotFoundError, json.JSONDecodeError) as e:
        print(f"Error loading config: {e}")
        return None
    
    user_id = config.get('user_id')
    if user_id is None:
        print("Error: user_id not found in config")
        return None
    
    return user_id
Vulnerable pattern
<?php
function authenticate_user($username, $password) {
    $mysqli = new mysqli("localhost", "user", "pass", "db");
    $stmt = $mysqli->prepare("SELECT id FROM users WHERE username = ? AND password = ?");
    $stmt->bind_param("ss", $username, $password);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    return $row['id'];
}
?>

Why it's vulnerable:
The code does not check whether the database connection succeeded, whether prepare() or execute() succeeded, or whether fetch_assoc() returned a valid row. If any step fails, the code may return null or trigger a fatal error, and the caller cannot distinguish between "user not found" and "database error."

Fixed pattern
<?php
function authenticate_user($username, $password) {
    $mysqli = new mysqli("localhost", "user", "pass", "db");
    
    if ($mysqli->connect_error) {
        error_log("Database connection failed: " . $mysqli->connect_error);
        return false;
    }
    
    $stmt = $mysqli->prepare("SELECT id FROM users WHERE username = ? AND password = ?");
    if (!$stmt) {
        error_log("Prepare failed: " . $mysqli->error);
        return false;
    }
    
    $stmt->bind_param("ss", $username, $password);
    if (!$stmt->execute()) {
        error_log("Execute failed: " . $stmt->error);
        return false;
    }
    
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    
    if ($row === null) {
        return false;
    }
    
    return $row['id'];
}
?>

05Prevention Checklist

Check return values and status codes
— Every function call that can fail (file I/O, network, database, memory allocation) must have its result inspected before use.
Use try-catch blocks
— Wrap operations that throw exceptions and handle them explicitly; do not let exceptions propagate silently.
Validate data before use
— After retrieving data from an operation, confirm it is in the expected format and state (not null, not empty, correct type).
Log errors appropriately
— Record failures in a way that helps debugging without exposing sensitive information to end users.
Fail safely
— When an error is detected, either recover gracefully or fail in a way that prevents further damage (e.g., roll back a transaction, close a connection).
Test error paths
— Write unit tests that deliberately trigger failures (missing files, network timeouts, invalid input) to ensure error handling works as intended.

06Signs You May Already Be Affected

Look for application crashes or unexpected behavior that correlates with specific conditions (file missing, network unavailable, database down). Check logs for unhandled exceptions or stack traces that suggest operations proceeded with invalid state. Review code for functions that return values without checking whether the underlying operation succeeded — this is a common sign that error conditions are being ignored.

07Related Recent Vulnerabilities