Guide

OpenAPI breaking changes: a reference with examples

Last checked 2026-11-0913 min read

An OpenAPI breaking change is a change to a spec after which a client built against the old version can fail. Removing things, adding requirements and narrowing what is accepted are breaking; adding optional things is not. This page lists the common cases, each with a before/after snippet and the rule ID that oasdiff, the open-source diff tool, reports for it, plus the cases where tools and teams disagree. Graphwake alerts the teams that use an endpoint when one of the breaking cases reaches the spec, already in the pull request when the spec comes through its CI step.

Exampleopenapi.yaml · before → after
− before+ after
/orders/{id}:  get:    parameters:      - name: currency        in: query        schema: { type: string }-       required: false+       required: true    responses:      "200":        description: OK        content:          application/json:            schema:              type: object              properties:                id: { type: string }-               total: { type: number }
  • BreakingNew required parameter currency
  • BreakingResponse field total removed
GET /orders/{id}reaches checkout and mobile-app
A removed response field is a breaking change. Graphwake tells the teams that subscribed to the endpoint.
On this page
  1. How to read this list
  2. Quick reference
  3. What Graphwake counts as breaking
  4. Breaking changes in responses
  5. Breaking changes in requests
  6. It depends: warnings
  7. Not breaking
  8. Where tools disagree
  9. When oasdiff alone is enough
  10. Next steps
  11. Questions

How to read this list

Every entry uses the same example orders API. The snippet shows the part of the spec that changed, before and after, as two YAML documents. The caption names the rule oasdiff reported when we ran the full before and after specs through oasdiff 1.34.1 on 2026-11-09, and its level:

  • error: a definite breaking change. oasdiff’s test is whether “a consumer that followed the old contract can stop working under the new one”.
  • warning: may break, but the spec does not say enough to decide.
  • info: not breaking.

oasdiff has hundreds of checks; this page covers the ones most pull requests hit. The full catalogue is on oasdiff’s breaking-changes reference, and oasdiff checks changelog prints it for the version you have installed.

bash
oasdiff breaking base.yaml revision.yaml
oasdiff changelog base.yaml revision.yaml

# Compare with the spec on main, straight from git
oasdiff changelog main:openapi.yaml openapi.yaml
Run the same check on your own specs. breaking shows errors and warnings; changelog shows every level.

Quick reference

Changeoasdiff ruleoasdiff levelGraphwake by default
Remove an endpointapi-removed-without-deprecationerrorAlert
Remove an endpoint that was deprecatedapi-removed-with-deprecationinfoAlert
Remove a required response fieldresponse-required-property-removederrorAlert
Remove an optional response fieldresponse-optional-property-removedinfoAlert
Change a response field’s typeresponse-property-type-changederrorAlert
A response field becomes optionalresponse-property-became-optionalerrorAlert
Add an enum value to a response fieldresponse-property-enum-value-addederrorAlert
Remove a success statusresponse-success-status-removederrorAlert
Add a required parameternew-required-request-parametererrorAlert
A parameter becomes requiredrequest-parameter-became-requirederrorAlert
Add a required request fieldnew-required-request-propertyerrorAlert
A request field becomes requiredrequest-property-became-requirederrorAlert
Change a request field’s typerequest-property-type-changederrorAlert
Remove an accepted enum valuerequest-property-enum-value-removederrorAlert
Lower a request field’s maxLengthrequest-property-max-length-decreasederrorAlert
Remove a query parameterrequest-parameter-removedwarningQuiet unless the team opts in
Remove a request fieldrequest-property-removedwarningQuiet unless the team opts in
Add an endpointendpoint-addedinfoQuiet unless the team opts in
Add an optional parameternew-optional-request-parameterinfoQuiet unless the team opts in
Add an optional request fieldnew-optional-request-propertyinfoQuiet unless the team opts in
Add a response fieldresponse-optional-property-addedinfoQuiet unless the team opts in
Accept a new enum valuerequest-property-enum-value-addedinfoQuiet unless the team opts in
Raise a request field’s maxLengthrequest-property-max-length-increasedinfoQuiet unless the team opts in
A request field becomes optionalrequest-property-became-optionalinfoQuiet unless the team opts in
Remove an enum value from a response fieldresponse-property-enum-value-removedinfoQuiet unless the team opts in

“Alert” means Graphwake tells the teams subscribed to that endpoint. The two rows where Graphwake alerts on an oasdiff info are explained under where tools disagree.

What Graphwake counts as breaking

Graphwake counts these as breaking, among others: removed endpoints, removed or retyped response fields, response fields that become optional, new response enum values, removed success statuses, new or newly required parameters and request fields, retyped request fields, and narrower accepted values. It also always alerts on removed response fields and removed endpoints, including the two cases under where tools disagree. Changes that cannot break a client stay quiet unless a team opts in.

  • Only the teams that use it

    Consumer teams subscribe to the endpoints they call. A breaking change alerts those teams, in Slack, Microsoft Teams or email.

  • Removals always alert

    A removed endpoint or response field breaks a caller, deprecated or not, optional or not. Graphwake alerts on every one.

  • Quiet by default

    Non-breaking changes do not alert anyone. A team can opt in to every change on the endpoints it uses.

  • Impact before the merge

    Each change set gets a 0-100 impact score from the breaking changes, the teams and people they reach, and blind spots.

See who your next breaking change reaches.Paste a spec URL or add one CI step. 7-day free trial. Cancel before day 7 and you pay nothing.
Start free trial

Breaking changes in responses

A client reads the response. Anything it relied on that disappears, changes shape or takes a value the client has never seen can make it fail.

Remove an endpoint

Every call to it now gets a 404 or 405. This is the most visible break and the easiest to miss in review when the operation sits in a long file.

yaml
# Before
/orders:
  get:
    responses:
      "200":
        description: Orders
  post:
    responses:
      "201":
        description: Created
---
# After
/orders:
  get:
    responses:
      "200":
        description: Orders
/orders path item (a Paths Object). POST /orders is gone. oasdiff: api-removed-without-deprecation (error).

Remove a required response field

A client that reads total gets nothing. Depending on the language, that is a crash, a null shown to a user or a wrong sum.

yaml
# Before
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: number

---
# After
type: object
required: [id]
properties:
  id:
    type: string
GET /orders/{id}, 200 response schema (a Schema Object). oasdiff: response-required-property-removed (error).

Change a response field’s type

A client that parses total as a number fails on a string, even when the string holds the same digits.

yaml
# Before
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: number

---
# After
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: string
GET /orders/{id}, 200 response schema (a Schema Object). oasdiff: response-property-type-changed (error).

A response field becomes optional

The field is still there today, but the spec no longer promises it. Clients that assumed it is always present have to handle its absence.

yaml
# Before
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: number

---
# After
type: object
required: [id]
properties:
  id:
    type: string
  total:
    type: number
GET /orders/{id}, 200 response schema (a Schema Object). oasdiff: response-property-became-optional (error).

Add an enum value to a response field

This one surprises people: it is an addition, and oasdiff still calls it breaking. A client with an exhaustive switch over status has no branch for refunded. See where tools disagree.

yaml
# Before
properties:
  status:
    type: string
    enum: [paid, shipped]
---
# After
properties:
  status:
    type: string
    enum: [paid, shipped, refunded]
GET /orders/{id}, 200 response schema (a Schema Object). oasdiff: response-property-enum-value-added (error).

Remove a success status

Moving from 200 to 201 looks cosmetic. A client that checks for exactly 200 now treats every success as a failure.

yaml
# Before
"200":
  description: Created
---
# After
"201":
  description: Created
POST /orders responses (a Responses Object). oasdiff: response-success-status-removed (error).

Breaking changes in requests

A client sends the request. Anything that turns a request that was valid yesterday into an invalid one is breaking, whether or not your server would still accept it today.

Add a required parameter

Every existing caller omits status, so every existing call is now invalid.

yaml
# Before
- name: limit
  in: query
  schema:
    type: integer
---
# After
- name: limit
  in: query
  schema:
    type: integer
- name: status
  in: query
  required: true
  schema:
    type: string
GET /orders parameters (a list of Parameter Objects). oasdiff: new-required-request-parameter (error).

A parameter becomes required

Same effect as adding one: callers that left it out now send an invalid request.

yaml
# Before
- name: status
  in: query
  required: false
  schema:
    type: string
---
# After
- name: status
  in: query
  required: true
  schema:
    type: string
GET /orders parameters (a list of Parameter Objects). oasdiff: request-parameter-became-required (error).

Add a required request field

Existing clients do not send currency. If the server enforces the schema, they get a 400.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string

---
# After
type: object
required: [items, currency]
properties:
  items:
    type: array
    items:
      type: string
  currency:
    type: string
POST /orders request body schema (a Schema Object). oasdiff: new-required-request-property (error).

A request field becomes required

Clients that treated coupon as optional and left it out now send invalid bodies.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string

---
# After
type: object
required: [items, coupon]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string
POST /orders request body schema (a Schema Object). oasdiff: request-property-became-required (error).

Change a request field’s type

Clients send quantity as a number; the spec now wants a string.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  quantity:
    type: integer

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  quantity:
    type: string
POST /orders request body schema (a Schema Object). oasdiff: request-property-type-changed (error).

Remove an accepted enum value

A client that sends GBP is now outside the contract.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  currency:
    type: string
    enum: [EUR, USD, GBP]

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  currency:
    type: string
    enum: [EUR, USD]
POST /orders request body schema (a Schema Object). oasdiff: request-property-enum-value-removed (error).

Lower a request field’s maxLength

A 20-character coupon code was valid; now it is not. Any narrowed constraint (a lower maximum, a higher minimum, a stricter pattern) works the same way.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string
    maxLength: 64

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string
    maxLength: 16
POST /orders request body schema (a Schema Object). oasdiff: request-property-max-length-decreased (error).

It depends: warnings

oasdiff reports these as warnings. Old clients keep sending the value; most servers ignore what they no longer read, but a server that rejects unknown fields or parameters will now fail those calls. Check how your server treats extra input before you decide.

Remove a query parameter

Callers still send status. Whether that breaks depends on the server.

yaml
# Before
- name: limit
  in: query
  schema:
    type: integer
- name: status
  in: query
  required: false
  schema:
    type: string
---
# After
- name: limit
  in: query
  schema:
    type: integer
GET /orders parameters (a list of Parameter Objects). oasdiff: request-parameter-removed (warning).

Remove a request field

Callers still send coupon. With additionalProperties: false or strict validation on the server, that request now fails.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
POST /orders request body schema (a Schema Object). oasdiff: request-property-removed (warning).

Not breaking

Additions and relaxed rules. A client built against the old spec keeps working, provided it ignores response fields it does not know, which most JSON clients do by default.

Add an endpoint

Nobody calls it yet, so nobody can break.

yaml
# Before
/orders:
  get:
    responses:
      "200":
        description: Orders
---
# After
/orders:
  get:
    responses:
      "200":
        description: Orders
/orders/{id}/receipt:
  get:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
    responses:
      "200":
        description: The receipt
Paths Object. GET /orders/{id}/receipt is new. oasdiff: endpoint-added (info).

Add an optional parameter

Existing calls leave it out, which is allowed.

yaml
# Before
- name: limit
  in: query
  schema:
    type: integer
---
# After
- name: limit
  in: query
  schema:
    type: integer
- name: status
  in: query
  required: false
  schema:
    type: string
GET /orders parameters (a list of Parameter Objects). oasdiff: new-optional-request-parameter (info).

Add an optional request field

Existing bodies without coupon stay valid.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string
POST /orders request body schema (a Schema Object). oasdiff: new-optional-request-property (info).

Add a response field

Clients that ignore unknown fields see no difference. A client that rejects unknown fields (a strict deserializer) does, so know your clients.

yaml
# Before
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: number

---
# After
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: number
  note:
    type: string
GET /orders/{id}, 200 response schema (a Schema Object). oasdiff: response-optional-property-added (info).

Accept a new enum value

Clients keep sending the values they already send.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  currency:
    type: string
    enum: [EUR, USD]

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  currency:
    type: string
    enum: [EUR, USD, GBP]
POST /orders request body schema (a Schema Object). oasdiff: request-property-enum-value-added (info).

Raise a request field’s maxLength

Every value that was valid is still valid. The same goes for any loosened constraint.

yaml
# Before
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string
    maxLength: 16

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  coupon:
    type: string
    maxLength: 64
POST /orders request body schema (a Schema Object). oasdiff: request-property-max-length-increased (info).

A request field becomes optional

Clients that send it can keep sending it.

yaml
# Before
type: object
required: [items, currency]
properties:
  items:
    type: array
    items:
      type: string
  currency:
    type: string

---
# After
type: object
required: [items]
properties:
  items:
    type: array
    items:
      type: string
  currency:
    type: string
POST /orders request body schema (a Schema Object). oasdiff: request-property-became-optional (info).

Remove an enum value from a response field

The client stops receiving a value it already handled. Its code for that value goes unused, but nothing fails.

yaml
# Before
properties:
  status:
    type: string
    enum: [paid, shipped, refunded]
---
# After
properties:
  status:
    type: string
    enum: [paid, shipped]
GET /orders/{id}, 200 response schema (a Schema Object). oasdiff: response-property-enum-value-removed (info).

Where tools disagree

Most of this list is not controversial. Three cases are, and it is worth deciding them on purpose instead of by tool default.

Adding an enum value to a response field

oasdiff calls it breaking (response-property-enum-value-added, shown above). GitHub’s Octokit guidance lists adding enum string constants as non-breaking. If your clients are written to handle unknown values, oasdiff supports marking the field with x-extensible-enum, which allows new values in responses.

Removing an optional response field

oasdiff reports it as info: an optional field may be absent anyway, so a correct client already copes. In practice clients read the optional fields they see. Graphwake treats removing any response field as breaking and alerts.

yaml
# Before
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: number
  note:
    type: string

---
# After
type: object
required: [id, total]
properties:
  id:
    type: string
  total:
    type: number
GET /orders/{id}, 200 response schema (a Schema Object). oasdiff: response-optional-property-removed (info).

Removing an endpoint after deprecating it

oasdiff reports it as info, because the removal was announced. Every caller that has not moved yet still breaks, so Graphwake alerts on any removed endpoint.

yaml
# Before
/orders:
  get:
    responses:
      "200":
        description: Orders
  post:
    deprecated: true
    responses:
      "201":
        description: Created
---
# After
/orders:
  get:
    responses:
      "200":
        description: Orders
/orders path item (a Paths Object). POST /orders was deprecated, then removed. oasdiff: api-removed-with-deprecation (info).

If your team draws the line differently, oasdiff lets you change the level of any check with --severity-levels and a file of rule IDs and levels.

Good to know

When oasdiff alone is enough

  • Every consumer of the API is in the same repository or team, so the diff in the pull request reaches everyone who needs it.
  • You want a hard stop in CI: oasdiff breaking --fail-on ERR exits non-zero on a breaking change.
  • You want to tune or extend the rules yourself.

Next steps

Questions

What is a breaking change in OpenAPI?

A change to the spec after which a client built against the old version can fail: removing an endpoint or response field, adding a required parameter or request field, changing a type, or narrowing the values a request may contain.

Is adding a field a breaking change?

Adding an optional request field or a response field is not. Adding a required request field is, because existing clients do not send it.

Is adding an enum value a breaking change?

In a request, no: clients keep sending the values they know. In a response, oasdiff says yes, because a client may not handle the new value. Teams whose clients tolerate unknown values often treat it as safe.

How do I detect OpenAPI breaking changes automatically?

Run oasdiff on the old and new spec, for example oasdiff breaking base.yaml revision.yaml, locally or in CI. It is free and open source. Graphwake’s CI step checks the spec on every pull request, and Graphwake alerts the teams that consume each changed endpoint.

Does Graphwake alert on non-breaking changes?

Not by default. Only breaking changes alert. A team can opt in to every change on the endpoints it subscribes to.

Pick an endpoint. See who it reaches. 7-day free trial. Cancel before day 7 and you pay nothing.

Start free trial

Sources and last verified

Sources checked on 2026-11-09.