The pandemic of incomplete OpenSSL error handling
- title
- The pandemic of incomplete OpenSSL error handling
- type
- summary
- summary
- Julian Andres Klode on the widespread anti-pattern of calling ERR_clear_error() to make OpenSSL errors go away โ discarding unrelated errors off the error stack
- tags
- security, software-quality, c
- sources
- openssl-error-handling-klode
- created
- 2026-07-21
- updated
- 2026-07-21
A short, pointed post by Julian Andres Klode (APT maintainer) arguing that a widely-copied OpenSSL "best practice" is a systemic, dangerous anti-pattern: sprinkling ERR_clear_error() around TLS operations to make inconvenient errors disappear.
The trigger
Someone reported that TLS in APT was failing on FIPS systems with MD5 errors and suggested wrapping TLS operations in ERR_clear_error(). Klode refused, on the principle that one component failing to handle its errors doesn't license discarding all errors somewhere else โ the program should have failed earlier, or discarded the specific error only once it was determined safe. He then discovered this workaround has been treated as a best practice for years: codebases everywhere call ERR_clear_error() before TLS operations, and OpenSSL upstream itself suggests doing so.
The core mistake: OpenSSL has an error stack
The root problem is that many authors don't realize OpenSSL maintains a stack of errors, not a single error slot. Two anti-patterns follow:
- Blanket clearing. Call
ERR_clear_error()before a TLS operation to wipe whatever is on the stack. This silently discards unrelated errors that were left there by earlier code โ errors that may signal a real problem. The error that caused the entry should be fixed (or handled where it occurred), not swept away later because it's now inconvenient. - Check-the-top-then-discard. Call an OpenSSL operation, inspect the top-level error, and if it's deemed "not too bad," discard the whole stack. Same defect: unrelated errors below the top get silently thrown away.
Both patterns convert "an error happened somewhere" into "no error," which is exactly the failure that breaks trust in security-sensitive code. This is the error-stack-anti-pattern โ discarding an entire error channel to suppress one entry.
The fix
Use OpenSSL's mark mechanism to scope your own error context instead of clearing globally: ERR_set_mark() to push a mark, then ERR_pop_to_mark() to pop back to it โ a guard around a group of OpenSSL operations that removes only the errors you generated, leaving anything from other code intact. Klode's call to action: audit your codebase for every ERR_clear_error() call and determine whether it's safe or one of the bad patterns above; and to OpenSSL's authors, stop encouraging practices that "fundamentally break any trust in software."
Why it matters beyond OpenSSL
The general lesson is about error-handling hygiene at library boundaries: when a library exposes a shared, stateful error channel, "clear it so my happy path works" is nearly always wrong, because you're discarding information that belongs to other code. Relatedly, it's the kind of subtle, easy-to-get-wrong C error handling that reviewing-ai-code and testing-heavy-no-review-workflow both flag as hard to catch by review alone โ the anti-pattern spread precisely because it looks locally correct and passes review. Compare the broader supply-chain-and-correctness concerns in supply-chain-security.