Ground Truth

Mars Climate Orbiter: Assert Your Units at Every Boundary

Mostafa DhouibMostafa Dhouib··6 min read
The short answer

A ground software module computed thruster impulse in pound force seconds while the interface specification required newton seconds. Navigation consumed it as newton seconds, underestimating the effect by a factor of 4.45, and no test or interface check at the contractor boundary ever reconciled the two.

Mars Climate Orbiter: Assert Your Units at Every Boundary

The short answer. SM_FORCES produced pound force seconds where the interface specification required newton seconds, and navigation consumed the value as if it were newton seconds. The factor of 4.45 was never reconciled by any test or interface check at the contractor boundary, and the orbiter was destroyed on atmospheric entry.

This one gets told as a joke about the metric system. It is not a units story. It is a story about a boundary that two competent organisations each tested up to and neither tested across.

What actually happened

From the NASA Mars Climate Orbiter Mishap Investigation Board Phase I Report, 10 November 1999.

A ground software module named SM_FORCES, which handled small-forces and angular momentum desaturation data, computed thruster impulse in pound force seconds. The Software Interface Specification required that value in newton seconds.

Downstream navigation software consumed the number as newton seconds. One pound force second is about 4.45 newton seconds, so the navigation model read the thruster effect as roughly 22 percent of its true value.

SM_FORCES computes thruster impulse
in pound force seconds
The interface specification requires newton seconds
written down, never enforced
No units assertion at the handoff
the boundary nobody owned
Navigation consumes the value as newton seconds
under by a factor of 4.45, read as about 22 percent of true
Predicted periapsis drifts low across the cruise
Entry at roughly 57 km against a survivable limit near 85 km
spacecraft destroyed
One assertion on the units, or one physical plausibility check on thruster effect, goes red at the handoff.
FigureBoth sides were internally correct. The defect lived in the agreement between them, which is the one place neither team's tests were looking.

Each individual error was tiny. They accumulated across the cruise, and the predicted periapsis drifted steadily lower than the real one. The corrected reconstruction puts the actual entry at roughly 57 km altitude, against a survivable limit somewhere near 85 km. The spacecraft was destroyed.

The Board's stated root cause was the failure to use metric units in the coding of a ground software file used in trajectory models.

Both sides were correct

This is the part worth dwelling on, because it is what makes the failure hard rather than embarrassing.

SM_FORCES computed correct impulse values. In pound force seconds, its outputs were right. Navigation correctly consumed newton seconds and correctly propagated a trajectory from them. Neither module contained a bug in any sense a code review would find, and neither team's tests were inadequate for the thing those tests were scoped to.

The defect existed only in the agreement between them, and an agreement is not owned by either party to it. It was written down, in the interface specification, which required newton seconds. Writing it down was not enough, because a specification is a claim about what should happen and nothing was comparing it against what did.

The verification gap the Board identified is precisely this: the units mismatch was never reconciled by any test or interface check at the contractor boundary, and the end-to-end navigation checks that would have exposed a factor of 4.45 were not effectively performed.

Why a factor of 4.45 survived

A four-and-a-half-times error should be obvious, and its survival is the most useful detail in the case.

It survived because it was plausible. Thruster effect is not a quantity anybody has strong intuition about, the errors were small per event, and the accumulated drift looked like the kind of navigation uncertainty that missions routinely carry and correct. Nothing about the number screamed. Navigation teams did notice discrepancies and did discuss them, and the discussion never reached the conclusion that the input units were wrong, because that possibility sits outside the space of things a navigation model is normally debugged against.

That is the signature of the whole class. A units error does not throw. An encoding error throws on the first message and somebody fixes it before lunch. A units error returns a well-formed number of the right type in the right range, and it can survive to the end of a mission. The loud failures are cheap and the quiet ones are expensive, which is the reverse of how most interface documentation is prioritised.

Would a negative control have caught it

Yes, and there are two versions with different costs.

Assert the units at the handoff. The interface specification already said newton seconds. Turn that sentence into a check that runs: tag the value, or verify its range against physical expectation, and fail when the producer's units do not match what the consumer requires. This is a negative control at a team and contractor boundary rather than inside a module, which is exactly why neither side's unit tests could have found it.

Run a physical plausibility check on the result. Cheaper and cruder. Thruster impulse of a known burn duration on a known thruster has an expected magnitude. A value off by 4.45 fails a loose bound. You do not need to know which side is wrong to know something is.

Either one goes red at the boundary, on the ground, long before Mars.

The general rule

Assert units and interface properties at every team and contractor boundary. That is one line in what good verification looks like, and it maps one-to-one to a lost spacecraft.

The properties worth asserting are wider than units, and they are ranked by how quietly they fail rather than by how important they sound: units, byte or word order, which fields are optional, behaviour on partial success, and who owns the agreement. Every one of those returns a plausible value rather than an exception when it is wrong.

Note the shape it shares with the CrowdStrike outage, which looks like a completely different incident. A producer and a consumer disagreed about a contract, the disagreement was invisible because nothing compared the two sides, and each side was internally correct the whole time. There, it was a field count. Here, it was units. The structural failure is identical, and so is the fix: express the contract as something that can go red.

The general argument is in a test that has never failed is a decoration. For working out what actually crosses one of your own boundaries, and which disagreements would fail silently, the interface contract diff is built for exactly that, with units at the top of the severity ordering for the reason above.

FAQ

What caused the Mars Climate Orbiter to be lost? A ground software file produced thruster impulse in pound force seconds where the interface specification required newton seconds. Navigation consumed it as newton seconds, underestimating the effect by the conversion factor of 4.45, and the accumulated trajectory error put entry at roughly 57 km against a survivable limit near 85 km.

Why did nobody catch the units mismatch? Because no test or interface check at the contractor boundary reconciled the two, and end-to-end navigation checks that would have exposed the discrepancy were not effectively performed. Both modules were internally correct, so neither team's own tests were looking at it.

Why did a factor of 4.45 survive for months? Because it produced a plausible number rather than an error. The per-event effect was small, the accumulated drift resembled ordinary navigation uncertainty, and wrong input units sit outside the space of causes a navigation model is normally debugged against.

How do you prevent a units error between teams? Turn the interface specification into a check that can fail: assert units at the handoff, or run a physical plausibility bound on the value. The specification alone is a claim about what should happen, and nothing was comparing it to what did.

Was this a coding error or a process error? Neither module was buggy. The defect lived in the unverified agreement between them, which is a boundary that no unit test on either side can reach.

Free checklist
The Verification Diligence Checklist

Nine controls, each tied to a public failure that cost somebody nine figures or a life, with the four artifacts a verification data room holds and the one question an investor should ask in the room. Built to be printed and ticked only where somebody has actually watched the check go red.

One email, the resource, and nothing else unless you reply.
Carrying a program like this one?

Tell us the system, the stakes, and the date that matters. You get a straight technical reply from the person who would lead the work, within 24 hours.

Bring us the program