← OpenAPI Breaking Change Check
Why does adding an enum count as a breaking change?
A parameter that used to take any string now only accepts three of them, and the checker calls that breaking — even though all you did was add a list of values. That reading is correct, and the same one rule explains it, and every other verdict this tool gives.
An "addition" that narrows is still a narrowing
Before the enum existed, the parameter accepted any value of its declared
type: any string, any integer. Restricting it to ["standard",
"express", "overnight"] does not add to what the API accepts —
it shrinks it. Every value outside that list, including ones a real client
could have been sending and getting a valid response for, is now rejected.
The list itself is new; what changed for an existing client is that the API
got pickier. That is what openapi_diff.py and this page are
classifying as enum-added, and it is reported breaking for the
same reason a newly-required parameter is: something that used to work can
now fail.
This is a shallow, structural read of the parameter's own schema, not a request-by-request replay against real traffic — see how this compares to reading the spec diff by hand for what that trade gives up.
The one rule behind every verdict
Every change this checker reports, breaking or not, answers the same question: does this shrink what the API accepts from a client that has not changed, or take away something a client could already rely on? A "yes" is breaking. Everything that only adds an option, widens what is accepted, or lifts a restriction is non-breaking — even when the diff itself reads as an addition.
| Change | Verdict | Why |
|---|---|---|
| Enum added to a previously unrestricted parameter | Breaking | Narrows what the parameter accepts |
| Value added to an existing enum | Non-breaking | Every previously-valid value still works |
| Value removed from an existing enum | Breaking | A value a client could send is now rejected |
| Enum constraint removed entirely | Non-breaking | Everything that worked before still works, plus more |
| New required parameter | Breaking | A request that omitted it used to succeed |
| New optional parameter | Non-breaking | Omitting it still works exactly as before |
Read this way, the checker has no special case for enums at all — it is the same narrowing-vs-widening test applied to a parameter's value set instead of its required flag or type.
Common questions
Why is restricting a parameter to an enum breaking, when I only added a list of values?
Because before the enum existed, the parameter accepted any value of its declared type — any string, any integer. Restricting it to a fixed list narrows that acceptance: every value outside the new list is a value an existing client could have been sending, and validly, that the API now rejects. The list is new, but what the API accepts got smaller, which is what the checker is classifying.
So is adding a value to an existing enum also breaking?
No, and this is the case most people expect: adding a value to a parameter that already had an enum is reported non-breaking. A client sending one of the old values keeps working exactly as before — the set of accepted values only grew. The breaking case is the opposite direction: a value that used to be accepted disappearing from the list, or the list appearing where there was none before.
What is the one rule behind every verdict this checker gives?
Does this change shrink what the API will accept from a client that has not changed, or take away something a client could already rely on? If yes, it is breaking. If the change only adds an option, widens what is accepted, or removes a restriction, it is non-breaking. A new optional parameter, a new endpoint, and an enum constraint lifted entirely are all widening moves, so all three come back non-breaking even though two of them are literally additions to the spec.
Does removing the enum constraint entirely count as breaking?
No — that direction is reported non-breaking. A parameter that used to accept only a fixed list and now accepts any value of its type still accepts everything an existing client was already sending, plus more. Nothing that worked before stops working, which is why the checker treats lifting a restriction as widening rather than narrowing.
Want the tool itself, or how it compares to oasdiff's much deeper change catalogue? See OpenAPI Breaking Change Check, or how this compares to oasdiff.