Case file
- What happened: Between 1985 and 1987, the Therac-25 medical linear accelerator — a computer-controlled radiation therapy machine produced by Atomic Energy of Canada Limited — delivered at least six massive radiation overdoses to patients.
- Scale: At least three patients died as a direct result. Several received doses estimated at over 100 times the intended therapeutic amount.
- Root cause: A race condition in the control software allowed the high-energy beam to fire without the flattening filter in position. Hardware safety interlocks present in earlier models had been removed and replaced by software-only checks.
- The bill: Loss of life, withdrawal of the machine from service, and a permanent place in engineering curricula as the canonical case of software-safety failure.
Here is an uncomfortable observation from two decades of quality management: every time I have seen a hardware interlock replaced by a software check, nobody updated the risk register. Therac-25 is not a story about a bug. Bugs are inevitable. It is a story about what happens when you strip a layer of physical defence, write a promise in code, and treat that promise as physics.
The situation
Two predecessors — the Therac-6 and Therac-20 — used mechanical hardware interlocks: beam stops, switches, fail-safe mechanisms that physically prevented firing if the configuration did not match the plan. When AECL designed the Therac-25, they removed those interlocks and let software enforce safety alone. The reasoning was not reckless. Software is flexible, cheaper to maintain, and the control code had worked in the previous generation. But the Therac-25 made that code the sole layer of defence — not a controller feeding a safety layer. Nobody subjected that decision to a formal safety case.
The operator interface compounded the risk. Error messages were numeric codes — "Malfunction 54," "Malfunction 64" — with no text explaining severity or meaning. Operators encountered these codes routinely and learned to override them with a single keystroke. The machine trained its operators to ignore its own warnings.
How it unfolded
The race condition sat latent for roughly two years before it killed anyone. It required a specific sequence of rapid operator inputs — editing treatment parameters in quick succession, faster than the software's internal timing expected. The code then commanded the high-energy beam without positioning the flattening filter, the metal plate that spreads and attenuates the beam to a safe dose.
Patients received the unfiltered output of the linear accelerator. Some reported an immediate burning sensation. In at least one case, a patient asked whether the treatment was supposed to hurt, was reassured, and was treated a second time with the same fault — the operator saw the familiar "Malfunction" code, overrode it, and the machine reset for another attempt.
The failure was invisible to operators and intermittent in its triggering. Each hospital experienced the fault independently, with no central mechanism to correlate reports.
Root-cause anatomy
The technical root cause was a shared-variable race condition. The software handled concurrent tasks — operator interface, machine monitoring, beam control — through shared flags corruptible by interleaved execution paths. Assembly language on a PDP-11. No formal verification, no static analysis, no independent review of the safety-critical logic. Concurrency bugs of this class are notoriously hard to reproduce because they depend on precise timing, which is exactly why testing alone can never certify safety-critical software.
The organisational root cause was worse. AECL had performed a fault analysis, but it assumed the software was reliable by design rather than treating the code itself as a failure mode. The reasoning: the control software had worked in the Therac-20, so it would work here. They ignored that the Therac-20 carried hardware interlocks as backup. Nobody asked what a proper hazard analysis demands: what protects the patient now that the physical safety net is gone?
A software interlock is a promise written in code. A hardware interlock is a promise enforced by physics. Only one survives a race condition your test plan never anticipated.
Where the quality system failed
The PFMEA treated software as a control measure, not as a failure mode. The "current control" column listed the software logic — the same logic that contained the bug. You cannot list the failure mechanism as the control for that same mechanism. That is a circular argument that passes audit only when nobody reads the document.
Removing the hardware interlocks from the Therac-20 design was a change with direct safety implications. Under IATF 16949, AS9100, or any functional safety standard worth the paper it is printed on, that change should have triggered a fresh hazard analysis, a new FMEA, and a formal safety case demonstrating equivalence. It did not — not with the depth the change demanded.
The human-factors failure was just as predictable. An error code that operators routinely override is not an operator error — it is a system failure mode. "Malfunction 54" paired with a one-key override is a usability defect that a proper review would have flagged before the first patient was treated.
What would have caught it
Several quality disciplines, applied at the right gate, would have caught this.
A PFMEA line for "software control fails" — with severity rated against an unfiltered beam at full power — would have driven the team to retain a hardware interlock or build independent software redundancy. A formal code review of safety-critical paths by someone other than the author, specifically hunting for concurrency hazards, would have found the race condition. Race conditions are findable when someone is looking. A usability FMEA treating "operator overrides cryptic error" as a failure mode rather than a training deficiency would have caught the interface defect. And a VDA 6.3 or AS9100 process audit of the software lifecycle would have flagged the absence of formal verification and traceable requirements.
My take
I see the Therac-25 pattern on a smaller scale more often than I would like. In aerospace manufacturing, I deal with software-controlled processes where a PLC or MES integration governs safety-critical steps — torque application, fastener seating, seal verification. The temptation is always the same: let the software enforce it, remove the physical check, save cycle time. The pushback is always the same too: "The software has been validated."
Validated against what? Against test cases the developer thought of — not the ones the operator will produce under fatigue on a Thursday afternoon.
My rule, forged in greenfield plants and audit rooms: software is a control, never the only control. Every safety-critical function gets at least two independent layers — if one is software, the other is physical or procedural. The 97% lead-time reduction I delivered at Airbus through routing verification KPIs worked precisely because the digital system sat on top of physical controls, not in place of them.
What this means on your floor
- If software is your only safety interlock, you do not have a safety interlock — you have an assumption.
- Any change that removes a physical safeguard triggers a full PFMEA refresh, not a signature on a change-request form.
- Error codes your operators routinely override are not operator errors. They are usability defects that belong in your corrective-action backlog.
- Audit your software development lifecycle with the same rigour you audit a supplier's welding process. If there is no formal verification, there is no safety case.
Three patients died because engineers removed a piece of hardware and replaced it with a line of code no one independently verified. The race condition was the mechanism. The absence of defence-in-depth was the cause. If your most important control fails, what catches it? If the answer is nothing, the design is not finished.