Rule
Handle errors in catch blocks. 
Empty catch blocks silently swallow errors, making debugging difficult. 
Supported languages: Java, C, C++, PHP, JavaScript, TypeScript, Go, PythonIntroduction
Empty catch blocks are one of the most dangerous anti-patterns in production code. When exceptions are caught but not handled, the error disappears without a trace. The application continues running with corrupted state, invalid data, or failed operations that should have stopped execution. Users see silent failures where features don't work but receive no error messages. Operations teams have no logs to debug with. The only indication something is wrong comes hours or days later when cascading failures make the system unusable.
Why it matters
Debugging and incident response: Empty catch blocks eliminate error logs. Engineers have no stack trace, error message, or indication of when or where the failure occurred, making issues nearly impossible to reproduce.
Silent data corruption: When database operations or API calls fail inside empty catch blocks, the application continues as if they succeeded. Records are partially updated, transactions are incomplete, and by the time corruption is discovered, the audit trail is gone.
Security vulnerabilities: Empty catch blocks mask security failures like authentication errors or authorization checks. An attacker triggering an exception in a security-critical path might bypass protections entirely if the error is silently swallowed.
Cascading failures: When errors are swallowed, the application continues in an invalid state. Subsequent operations depending on the failed operation's result will also fail, creating a chain of failures that misleads engineers from the actual root cause.
Code examples
❌ Non-compliant:
async function updateUserProfile(userId, profileData) {
    try {
        await db.users.update(userId, profileData);
        await cache.invalidate(`user:${userId}`);
        await searchIndex.update(userId, profileData);
    } catch (error) {
        // TODO: handle error
    }
    return { success: true };
}Why it's wrong: If any operation fails, the error is silently ignored and the function returns success. The database might be updated but cache invalidation could fail, leaving stale data. Or the search index update fails, making the user unsearchable, with no log or alert to indicate the problem.
✅ Compliant:
async function updateUserProfile(userId, profileData) {
    try {
        await db.users.update(userId, profileData);
        await cache.invalidate(`user:${userId}`);
        await searchIndex.update(userId, profileData);
        return { success: true };
    } catch (error) {
        logger.error('Failed to update user profile', {
            userId,
            error: error.message,
            stack: error.stack
        });
        throw new ProfileUpdateError(
            'Unable to update profile',
            { cause: error }
        );
    }
}
Why this matters: Every error is logged with context, providing debugging information. The error propagates to the caller, allowing proper error handling at the appropriate level. Monitoring systems can alert on these errors, and the application fails fast rather than continuing with invalid state.
Conclusion
Empty catch blocks are never acceptable in production code. Every caught exception needs logging at minimum, and most need to propagate to callers or trigger specific recovery actions. If you genuinely need to ignore an error, document why with a comment explaining the business justification. The default should always be to handle errors explicitly, not silently discard them.
.avif)
