Caloogy Article
中文

Fail-closed: Better to Stop Than to Let Through When Errors Occur

Date
Oct 22, 2025
Series
Methodology
Reading
4 min
Topics
Fail closedFail safeAvailabilityCircuit breaker

The one-sentence version

Systems will always fail: data sources become unreachable, validation programs crash, or configuration files fail to load. In such cases, there are only two choices: fail-open (prioritizing availability) or fail-closed (prioritizing security). For decisions involving money, permissions, or identity, the default should be fail-closed: if it cannot be checked, treat it as failed. The difficulty lies in distinguishing "errors" from "normal empty results" and ensuring that fail-closed does not make the system so fragile that it stops at the slightest touch.

This article has three layers:

  1. Layer 1: The Intuition: Should electronic locks open or stay locked when the power goes out?
  2. Layer 2: Real-World Use: Firewalls, antivirus software, circuit breakers, and blacklist updates.
  3. Layer 3: Principles and engineering key points: Identifying "errors," protecting data sources, automatic loss prevention, and the cost of availability.

Layer 1: The Intuition

1.1 Power outage in an electronic lock

If the electronic access control system in a building loses power, the doors should:

There is no absolute "correct" answer; it depends on what you fear most when an error occurs: fear of people being unable to get out, or fear of bad actors getting in.

1.2 The same problem in software

def is_allowed(address):
    try:
        return address not in fetch_blocklist()
    except NetworkError:
        return True   # ← fail-open: if the list cannot be fetched, assume it is fine

This code looks "friendlier": the user can continue operating even during network jitter. However, if an attacker finds a way to make the blacklist service unreachable (e.g., by attacking it or interfering with the network), all checks become ineffective.

Changing to fail-closed:

    except NetworkError:
        return False  # If it can't be checked, assume it failed

Layer 2: Real-World Use

Scenario Typical Choice Reason
Firewall rules Default deny, allow only the whitelist A single missing rule is a hole
Financial transaction risk control timeout Deny or escalate to manual review The cost of letting one fraudulent transaction through is much higher than rejecting one legitimate transaction
Certificate Revocation Check (OCSP) failure Browsers usually fail-open ("soft fail") Otherwise, if a CA server jitters, all HTTPS on the web would break. This is why certificate revocation mechanisms are often criticized as being "virtually non-existent"
Electrical circuit breaker Disconnect during overload Better to have a power outage than a fire
Software circuit breaker Fail fast when downstream fails Prevents failure cascades from bringing down the entire system

The OCSP example illustrates: fail-closed is not free; it shifts the availability pressure onto the dependent services.


Layer 3: Principles and Engineering

3.1 First, identify "errors"

The most insidious form of fail-open is not except: return True, but treating an error as a normal empty result:

Looks like Actually is
This address is not in the list The list file is empty (download failed and wrote an empty file)
Balance is 0 The node returned an error, which was parsed as 0
No risk control rules matched The rule configuration failed to load
Signature verification passed The verification function was skipped due to an exception

Therefore: You must explicitly distinguish between "successfully obtaining an empty result" and "failing to obtain a result"; the latter must trigger an explicit error.

3.2 Protection for data source updates

External lists (fraudulent addresses, sanction lists, malicious domains) need regular updates. The update process itself can be attacked: an upstream repository could be poisoned to push an almost empty list; if the downstream automatically accepts it, all checks become useless.

Protective measures:

3.3 Stop when inconsistent

In multi-source cross-verification, when two independent sources provide different answers, the correct reaction is to stop, rather than picking the one that looks more convenient. This is the application of fail-closed in redundant systems.

3.4 Automatic loss prevention

A more aggressive form of fail-closed is: when the system detects it might have been compromised, it proactively shuts down services. For example, if the frontend page does not match the expected state, automatically take the page offline—it is better for everyone to be temporarily unable to use it than to allow anyone to sign something on a potentially tampered page (see The Bybit Incident and Frontend Integrity).

Design key points:

3.5 Cost and balance

Fail-closed makes a system "brittle": if any single dependency jitters, the user cannot use the system. Ways to balance this:


Summary

Layer Key points
Intuition What do you fear most when an error occurs? If it's bad actors getting in, lock the door
Principle Security decisions: If it cannot be checked, treat it as failed
Pitfalls Treating "errors" as "empty results"; lists being poisoned or shrunk
Balance Risk-based classification; redundancy for dependencies; clear failure communication; monitor rejection rates