TestMax
requirement driven autonomous testing tool
← Back to Blog
Test Cases

NATS 2023: The Flight Plan That Was Perfectly Valid

Ali Hashim·August 12, 2026·17 min read

Most software failure stories start with something going wrong. Bad data arrives. A patch gets missed. A deployment reaches seven servers instead of eight. There is a villain in the sequence somewhere, and once you find it the lesson writes itself.

The NATS incident of August 2023 has no villain. Every party in the chain did their job correctly. The input was valid. The software met its requirements. UK airspace still lost flight data processing for six hours on one of the busiest travel days of the year, at a cost of 75 to 100 million pounds.

That is a harder story to tell and a more useful one to read, because the failure mode it demonstrates is sitting in most enterprise test suites right now.

Key facts

  • On 28 August 2023, a valid flight plan caused the NATS FPRSA-R flight plan processing subsystem to hit a software exception and enter a fail safe state, halting automated flight data processing for UK airspace.
  • The flight plan complied fully with specification. It was filed correctly by an airline and transmitted correctly by EUROCONTROL, so no validation rule could have rejected it.
  • The backup system engaged, received the same input, and entered the same fail safe state, because it ran identical logic.
  • The fault occurred shortly after 08:30 BST and the system was restored at 14:30, an outage of roughly six hours.
  • More than 2,000 flights were cancelled across 28 and 29 August 2023, and more than 700,000 passengers were affected.
  • The UK Civil Aviation Authority estimated the total cost to passengers and air operators at between 75 and 100 million pounds.
  • FPRSA-R had been in service since 2018 and had never encountered the triggering condition before, meaning the coverage gap existed undetected for five years.
  • No flight safety events occurred. The fail safe design worked as intended. The failure was one of availability, not safety.

What happened on 28 August 2023

The date was a bank holiday Monday in England and Wales. NATS, the UK's air navigation service provider, had a change freeze in place precisely because of the date. No deployments, no upgrades, nothing in flight. Systems were running normally and technical staff were monitoring them according to standard procedure.

Shortly after 08:30 BST, EUROCONTROL's flight plan processing system passed a routine flight plan to a NATS subsystem called FPRSA-R. Its job is narrow and important. Read a filed flight plan, work out where the aircraft enters and leaves UK airspace, and hand those points to UK controllers so they can plan for it.

The flight was going from Los Angeles to Paris. Its route clipped UK airspace on the way in.

FPRSA-R could not process the plan. The software hit an exception and moved into a fail safe state, which meant flight plan data now had to be entered by hand. The backup engaged, met the same input, and did the same thing. Within moments both primary and secondary were offline, and a small number of people were typing flight plans manually on a bank holiday Monday.

The system came back at 14:30, after the software's manufacturer identified the fault. By then the operational damage was done, and it kept compounding into the following day as aircraft and crews sat in the wrong places.

Two points on the map called DVL

Waypoints and airfields worldwide carry short alphanumeric designators. They are meant to be unique. In practice they are not, and duplicates persist globally despite long running efforts by ICAO and other bodies to remove them.

The flight plan referenced DVL. So does Deauville in France. So does Devil's Lake in North Dakota. The two sit roughly 4,000 nautical miles apart.

FPRSA-R resolved the UK entry point correctly. The exit point matched a designator pointing at a location on the wrong side of the Atlantic, and the software recognised the geographic impossibility. That recognition is where the sequence began, not where it failed.

Here is the nuance most coverage of this incident skips, and the reason this article does not claim duplicate codes crashed the system. FPRSA-R was built to handle duplicate designators. Duplicates on their own were not enough to trigger the exception. The failure needed a specific and rare combination of conditions present in that one flight plan, and the duplicated designator was one ingredient rather than the cause. NATS described the circumstances as extremely rare and highly specific.

That distinction makes the case more useful, not less. A system that chokes on obviously bad input has a bug, and bugs get found. A system that handles the general case correctly for five years, then meets one legal combination it was never exercised against, has a test coverage gap. Coverage gaps do not announce themselves. They wait.

Why a valid input changes the entire lesson

There was nothing to reject here.

The flight plan complied with specification. An airline filed it normally. EUROCONTROL passed it on normally. No validation rule would have helped, because valid input is precisely what a validation layer exists to let through.

Strip away every defensive control and one thing remains that could have caught this before a real aircraft was involved. A test. Specifically, a test exercising that combination of conditions against FPRSA-R before production did it live.

That test did not exist, because nobody imagined it. In practice, "nobody imagined it" is the root cause behind a large share of production incidents in mature systems. Not carelessness, not missing process. A limit on what a human test author can hold in their head, which is a different problem and needs a different solution.

The backup failed for the same reason the primary did

FPRSA-R had a backup. It engaged as designed, received the same input, ran the same logic, hit the same exception, and entered the same fail safe state.

The distinction that matters:

  • Redundancy protects against component failure. A disk fills, a node dies, a network partitions, and the standby takes over cleanly.
  • Redundancy does not protect against a logic gap. A redundant copy of the software contains a redundant copy of the gap, so two identical systems given identical input fail identically every time, by design.
  • Failover time is irrelevant when the failure is deterministic. The standby will reach the same exception as fast as it can process the input.

Teams often discover this during an incident review, and the conversation tends to be uncomfortable, because resilience posture documents rarely separate the two risks. "We have failover" reads as coverage for both. It is coverage for one.

There is an operational footnote worth carrying too. The CAA's review found that the engineer able to reset the system was working from home, could not resolve it remotely, and reached the site more than three hours after the incident started. The technical fault lasted minutes. The outage lasted six hours. That gap is a recovery process problem rather than a software problem, and it is the number that ends up in the press.

What an edge case actually is, and why suites miss them

An edge case is an input or condition at the boundary of a system's expected operating range. Technically permitted, rarely encountered, and frequently unhandled.

Teams miss them for structural reasons rather than reasons of skill. Test suites are written by people, and people write tests for behaviour they have pictured. The set of cases a human can picture is bounded by their experience of the system. The set of cases production can generate is bounded only by the specification. The space between those two sets is the coverage gap.

Three properties make that gap hard to see:

  • A missing test produces no signal. It does not fail, does not appear in a report, and does not degrade any metric. Coverage tooling reports which lines your tests execute, not which regions of the input space your tests explore, so a suite can show high line coverage while never exercising a single boundary condition.
  • The gap widens as systems age. Suites accumulate cases in response to defects, which makes them steadily better at failures you have already had and no better at failures you have not. FPRSA-R ran clean from 2018 to 2023. From inside the organisation, a coverage gap looks like five years of green builds.
  • The gap clusters at integration boundaries. The DVL collision was not a defect inside FPRSA-R alone. It emerged from the interaction between global waypoint data, an upstream system's output, and FPRSA-R's resolution logic. Each component behaved correctly. The combination belonged to nobody.

That last point has empirical support. NIST's work on combinatorial testing, published in Special Publication 800 142, found across multiple studied domains that most software failures were triggered by interactions between a small number of parameters, and that no failure examined required more than six parameters interacting. The practical implication is direct. Testing parameters in isolation leaves the majority of real failure modes untouched, which is exactly what happened here.

Negative testing, and the category most teams skip

Negative testing validates how a system behaves with inputs and conditions outside the happy path. Invalid data, boundary values, absent fields, contradictory combinations, and inputs that are legal but unusual. ISTQB treats boundary value analysis and equivalence partitioning as foundational black box techniques for exactly this reason.

Most teams do some of it. Very few do it systematically, because the negative case space is unbounded. A flight plan has one routine shape and an effectively infinite number of strange shapes that remain entirely legal.

Two categories get collapsed in practice, and separating them is most of the work:

  • Invalid input violates the specification. It should be rejected cleanly with a useful error. This is relatively easy to test, because the specification defines what invalid means and the cases write themselves.
  • Valid but unusual input complies fully with the specification and occupies a region of the input space nobody exercised. This is much harder to test, because nothing in the specification marks it as interesting and there is no rule to test against.

NATS sits squarely in the second category, and so do most incidents that surprise mature engineering organisations. Teams get good at the first category quickly, because those failures are loud and arrive early. The second stays silent for five years and then takes out a bank holiday.

The same failure shape, in systems you probably work on

The aviation context makes this feel remote. It is not. The pattern is a uniqueness assumption that holds almost always, until it does not.

  • Banking: payment reference collisions. Two counterparties generate the same reference on the same value date, and a reconciliation process built on the assumption of uniqueness either matches the wrong pair or halts. A frequent issue we see in financial services QA is that the uniqueness assumption lives in code comments and tribal knowledge rather than in a stated requirement, so there is nothing for a test to be derived from.
  • Healthcare: patient identifiers after a merger. Two hospitals combine, both had internally unique medical record numbers, and the merged record set does not. Every downstream integration built on the old assumption inherits a defect that may not surface for months, which in clinical systems is the worst possible detection lag.
  • eCommerce: SKU and variant collisions. After a catalogue migration or a marketplace integration, inventory decrements against the wrong item. Nothing errors. The failure surfaces as an unexplained stock discrepancy weeks later.
  • Multi tenant SaaS: identifiers unique per tenant used in a global context. This one deserves specific attention, because the failure mode in multi tenant architectures is cross tenant data exposure rather than a crash, so it can run undetected far longer than a fail safe would allow.
  • CI/CD: duplicate branch, tag or artefact names across parallel builds. The pipeline does not fail. It deploys the wrong artefact, and the deployment log looks completely normal.

Every one of these is the DVL problem wearing different clothes. An identifier was assumed unique, the assumption was never written down as a testable requirement, and no test existed for the case where it did not hold.

How to test for edge cases nobody has thought of

You cannot enumerate them by hand. That is the honest answer, and any approach starting with "engineers should think harder" fails the same way FPRSA-R did. What you can change is where your test cases come from.

  • Derive tests from the specification, not the implementation. Cases written by reading code inherit the code's assumptions, including the wrong ones. Cases derived from the requirement inherit the requirement's input space instead, so combinations nobody implemented surface as gaps rather than silence. The trade off is real. This only works if your requirements are good enough to derive from, which for many teams means fixing requirement quality before anything else.
  • Treat the input space as a domain to cover, not a list to enumerate. For each field, establish the boundaries. Minimum, maximum, empty, maximum length, wrong type, duplicated, and colliding with another field's value. Then test combinations, because per the NIST findings that is where real failures live.
  • Write an explicit test for every uniqueness assumption. Any time a design states that identifiers are unique, test what happens when they are not. Uniqueness is one of the most commonly assumed and least commonly enforced properties in software.
  • Use property based testing where the domain fits. Assert invariants that must hold for any valid input, then let a generator produce thousands of conforming inputs. This finds combinations no one would sit down and write. It suits parsers, protocol handlers and data transformation layers, and works poorly for workflow heavy UI paths, so scope it deliberately rather than adopting it everywhere.
  • Generate structured test data rather than reusing production samples. Production data reflects the input distribution you have already seen, which is the distribution that has never broken anything. Synthetic test data generation lets you deliberately construct the collisions and boundary combinations production has not produced yet.
  • Audit your redundancy honestly. For each redundant system, ask whether the redundancy covers infrastructure failure or logic failure. If the standby runs identical code, it covers the first only. Write that down rather than counting it twice in your resilience posture.
  • Rehearse the recovery path, not just the failure path. The NATS fail safe worked correctly. Recovery took six hours. Test who can restore a system, from where, how fast, and what happens when that person is not at their desk on a public holiday.
  • Feed production surprises back into the requirement. Patching the code closes one instance. Updating the requirement closes the class. Teams doing only the first keep meeting variations of the same defect and cannot work out why the regression suite never catches them.

Where requirement driven testing fits

Everything above shares one assumption, which is that somebody has the time and appetite to systematically explore an input space that grows combinatorially with every field added. Under release pressure, in most teams, that does not happen. Edge case coverage is the first work cut and the last work missed, right up until the point it is missed expensively.

This is the problem TestMax was built around. TestMax generates test cases from requirements rather than from scripts written against an existing implementation, which means the input space described by the requirement defines the coverage target. Boundary conditions, invalid combinations, and the valid but unusual permutations a human author would not enumerate at the end of a sprint are derived rather than remembered. The difference between that model and script based automation is covered in more depth in our comparison of requirement driven autonomous testing and traditional test automation.

Requirement Intelligence also flags where the requirement itself is ambiguous or incomplete. A specification stating that identifiers are unique, without stating what the system does when they are not, is underspecified. Validating acceptance criteria before implementation costs considerably less than discovering the gap through a real flight plan, a real payment reference, or a real patient record.

Because every generated case traces back to the requirement that produced it, coverage becomes a question you can answer. Not how much of our code do our tests execute, but which requirements have been validated, against which conditions, and where the gaps are. That is the question the NATS incident asked, five years after the system went live. It is the same question the Knight Capital deployment failure asked in 45 minutes, from the opposite direction.

If you want to see what requirement derived coverage looks like against your own specifications, Book a Demo.

Frequently asked questions

What caused the 2023 UK air traffic control failure?

A flight plan filed for a Los Angeles to Paris service triggered a software exception in FPRSA-R, the NATS subsystem that extracts UK airspace entry and exit points from filed flight plans. The plan was valid and within specification. A rare combination of conditions within it, including the duplicated designator DVL which refers to both Deauville in France and Devil's Lake in North Dakota, could not be processed, and the system entered a fail safe state.

Was the flight plan incorrect?

No. It conformed to specification and was filed and transmitted correctly. That is what separates this case from most software failure stories. There was no bad input to catch, so no validation layer could have prevented it. The failure came from a gap in what the software had been tested against.

Why did the NATS backup system fail as well?

The backup ran the same logic as the primary. Given the same input it hit the same exception and entered the same fail safe state. Redundancy protects against hardware and infrastructure failure. It does not protect against a logic gap, because a redundant copy of the software contains a redundant copy of the gap.

How long did the outage last and what did it cost?

The fault occurred shortly after 08:30 BST on 28 August 2023 and the system was restored at 14:30. More than 2,000 flights were cancelled across 28 and 29 August, more than 700,000 passengers were affected, and the UK Civil Aviation Authority estimated total cost to passengers and air operators at between 75 and 100 million pounds.

What is negative testing in QA?

Negative testing validates system behaviour with inputs and conditions outside the expected happy path, including invalid data, boundary values, missing fields and legal but unusual combinations. It is distinct from checking that invalid input gets rejected. The harder and more valuable category is input that fully complies with specification but occupies a region of the input space nobody exercised.

Can automated tests catch edge cases nobody has thought of?

Not if the cases are written by hand against an existing implementation, because those tests inherit the implementation's assumptions. Coverage improves when cases are derived from the requirement or generated across the input space, through property based testing, systematic boundary analysis, or requirement driven test generation, so that combinations no one enumerated still get exercised.

How do I tell whether my own test suite has this problem?

Look for three signals. Uniqueness assumptions that appear in design documents but have no corresponding test. Fields tested individually but never in combination with each other. And a suite whose new cases arrive almost entirely in response to defects, which means it is calibrated to the failures you have already had rather than the ones ahead of you.

References

  1. NATS, Major Incident Investigation Final Report, 28 August 2023, published via the UK Civil Aviation Authority. https://www.caa.co.uk/publication/download/23340
  2. NATS, Major Incident Preliminary Report, 4 September 2023.
  3. UK Civil Aviation Authority, Independent Review of the 28 August 2023 NATS flight planning system failure.
  4. NIST Special Publication 800 142, Practical Combinatorial Testing, Kuhn, Kacker and Lei.
  5. ISTQB Certified Tester Foundation Level syllabus, black box test techniques including equivalence partitioning and boundary value analysis.
  6. BBC News reporting on the CAA review findings, including cost estimates and engineer response timeline. https://www.bbc.co.uk/news/articles/ce8drx6v0ypo
Tags:software testing automationAI test automationtest automation toolqa automation services​
← Back to Blog