A TV repair technician in the UK, Allen Fleckney, keeps posting variations of the same video, and it keeps going viral. The setup never changes. A large, expensive television, a few years old, arrives dead. No picture. The owner has already been told it isn’t worth fixing and they should buy a new one. Fleckney opens it, finds that a single LED in the backlight strip has failed, bridges two pins so the firmware stops seeing the fault, and the set springs back to life with a perfectly good picture. It was reported by Cybernews and Digital Trends, and you can watch him do it on his own TV Repair Community channel.
Where the fault is#
The LED failing is normal. A backlight is a strip of dozens of LEDs, and over a few years one of them drifts out of tolerance and starts drawing a handful of milliamps more or less than spec. For a component like that, this is an ordinary, expected, end-of-life event.
The actual fault is a decision made in firmware. The moment the current on that rail moves outside a tight window, the set treats it as fatal and shuts everything down. One LED out of dozens takes the whole television with it, while the panel behind it still works.
That is a classification mistake, and it cost the owner a television.
What kind of failure this is#
When engineers argue about error handling, they usually argue about mechanism: exceptions versus
error codes versus std::expected
. That is the second question. The first one is what kind of failure
this is, and when that answer is wrong, no mechanism repairs it.
On my Advanced Modern C++ and Modern C++ Design courses I split failures three ways because the right response is different for each.
Bugs. A precondition was violated. Someone called the function wrong: a null pointer where the
contract said non-null, an index out of range, a broken invariant. This is a defect in the program
rather than an error to be handled. There is nothing to recover from because the program is
already in a state its own author declared impossible. For many functions, calling them with their
preconditions unmet is undefined behavior outright, so there isn’t even a defined error to report.
The C++ Core Guidelines deliberately keep this case away from exceptions: exceptions are “for error
handling only” (E.3), and a precondition is meant to be expressed with an assertion, Expects()
(I.6, “Prefer Expects()
for expressing preconditions”), rather than a throw
. A failed
precondition should stop the program rather than become something a caller catches and continues
past. Contracts, still in the C++26 draft (P2900), turn the same idea into first-class syntax: a
function declares its pre- and postconditions inline with the signature.
double safe_sqrt(double x)
pre (x >= 0) // caller's obligation
post (r: r >= 0); // this function's guarantee
When a caller breaks pre
, the default is to run a violation handler that terminates the program,
not to limp on with a bad result. That is exactly right for a bug: stop, capture the state, and get
it to a developer. Before C++26 you reach for the same behavior with an assertion. Reporting a bug as
a recoverable error only hides it until it corrupts something downstream.
Expected failures. Things that are neither bugs nor rare: bad user input, a file that isn’t
where the user typed it, a network peer that won’t answer, a database row that already exists.
Your code is working correctly and the world simply didn’t cooperate. These belong on ordinary
branches, in the normal flow of the code, handled by the immediate caller who is standing right
there and knows what to do. In modern C++ this is exactly what std::expected<T, E>
(from P0323) is
for: the failure lives in the return type, the caller can’t silently ignore it, and the happy path
still reads cleanly.
std::expected<Config, ParseError> parse(std::string_view text);
if (auto cfg = parse(input))
use(*cfg);
else
report(cfg.error()); // just another branch, not an exception
Genuinely exceptional failures. Rare, and the code that can do something useful about them is far up the call stack, not the immediate caller. This is what exceptions are for.
The weight is on its assigned task: the function was used correctly and still couldn’t deliver. The compiler’s own cost model assumes this is rare, roughly a one-in-a-hundred event or rarer. Reach for it on the common path and you pay for it, both in performance and in code you can’t easily follow.
Back to the television#
A backlight LED drifting out of spec after a few years is a textbook expected failure. It isn’t a bug in the firmware, no precondition was violated and the aging LED is a perfectly legitimate runtime input, and it isn’t a rare catastrophe either. The correct design is an ordinary branch: read the fault, log it, keep driving the panel, perhaps nudge brightness to compensate, and raise a quiet “service soon” flag. The hard shutdown should be reserved for the genuinely dangerous, a real overvoltage or a thermal runaway, something that could actually start a fire.
What the firmware does instead is take an expected, recoverable condition and route it straight down the path reserved for “stop everything now.” The hardware was still fine, and the classification is what turned a two-pin repair into landfill.
Fleckney frames this as deliberate planned obsolescence, and the manufacturers have not answered the accusation. I don’t need the conspiracy for the point to hold, and honestly the point is stronger without it. Maybe it is cynical design. Maybe it is a lazy safety cutoff someone shipped without thinking the failure modes through. The engineering lesson is the same either way: a routine, recoverable condition was classified as fatal.
Classify the failure first#
That is the very first guideline on the subject. Getting the taxonomy wrong is expensive, and the
cost grows the longer it goes unnoticed. Miscategorize a bug as a recoverable error and it hides in
production until it corrupts state. A routine failure classified as exceptional costs you either
runtime on a hot path or, as the television shows, a system that was working.
The mechanisms matter, and I teach them thoroughly: the basic, strong, and nofail exception-safety
guarantees, RAII, std::error_code
, the monadic interface on std::expected
. But every one of them
is downstream of the decision the TV firmware got wrong. Before you choose how to report a failure,
decide what the failure actually is. Is it a bug, an expected condition you should handle on a branch,
or a rare event you should let propagate? Most of the brittle systems I get called in to look at got
that one question wrong long before anyone wrote a try
or a catch
.
Have a thought or a counterexample? Discuss this post on LinkedIn .