How to validate OpenAPI spec
Run npx --yes @redocly/cli@2.52.1 lint openapi.yaml. A structurally valid document ends with a timing line and exit 0. Anything wrong prints one row per problem with a line and column, the rule that fired, and whether it counts as an error or a warning. Exit 1 means the run failed.
Why check this
A spec with an unresolved $ref still renders in a documentation viewer and still fails every client generator that reads it. Contract tests built on that spec then pass against nothing. Lint the file in CI on every pull request that touches it, and again before the spec is published to consumers.
Prerequisites
- Node 18 or later.
npxfetches the linter on first use and caches it. - Redocly CLI 2.52.1, pinned, because default rulesets change between major versions.
NO_COLOR=1in the shell, so the output matches the blocks below.- A spec file. The one used here is
openapi.yaml:
openapi: 3.1.0
info:
title: Orders API
version: 1.4.0
license:
name: Apache-2.0
url: https://www.apache.org/licenses/LICENSE-2.0
servers:
- url: https://api.orders.internal/v1
security:
- bearerAuth: []
paths:
/orders/{id}:
get:
operationId: getOrder
summary: Read one order
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: The order
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'404':
description: No such order
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
Order:
type: object
required: [id, total]
properties:
id:
type: string
total:
type: number
Steps
- Step 1.
Lint the spec with the ruleset the tool applies when no config file is present.
npx --yes @redocly/cli@2.52.1 lint --format=stylish openapi.yamlNo configurations were provided -- using built in recommended configuration by default. validating openapi.yaml... openapi.yaml: validated in 27ms …No problem rows appear between the two lines, which is what a clean run looks like.
- Step 2.
Read the exit code, because that is what a pipeline acts on.
echo $?0 - Step 3.
Copy the spec to
openapi-badref.yaml, change the$refon line 29 to#/components/schemas/Ordr, and lint that copy.npx --yes @redocly/cli@2.52.1 lint --format=stylish openapi-badref.yamlvalidating openapi-badref.yaml... openapi-badref.yaml: 29:17 error no-unresolved-refs Can't resolve $ref 38:5 warning no-unused-components Component: "Order" is never used. openapi-badref.yaml: validated in 35ms …The second row is the useful one. A component that nothing references is the signature of a typo in a pointer, not of dead code.
- Step 4.
Copy the spec to
openapi-broken.yaml, delete theparametersblock and thedescriptionunder the200response, then lint it.npx --yes @redocly/cli@2.52.1 lint --format=stylish openapi-broken.yamlvalidating openapi-broken.yaml... openapi-broken.yaml: 18:9 error struct The field `description` must be present on this level. 14:5 error path-parameters-defined The operation does not define the path parameter `{id}` expected by path `/orders/{id}`. openapi-broken.yaml: validated in 41ms … - Step 5.
Re-run the same file with the minimal ruleset, which is the one to put in CI while a spec is being cleaned up.
npx --yes @redocly/cli@2.52.1 lint --extends=minimal --format=stylish openapi-broken.yamlvalidating openapi-broken.yaml... openapi-broken.yaml: 18:9 error struct The field `description` must be present on this level. 14:5 warning path-parameters-defined The operation does not define the path parameter `{id}` expected by path `/orders/{id}`. openapi-broken.yaml: validated in 44ms …structstays an error andpath-parameters-defineddrops to a warning. That difference is the line between "not a valid OpenAPI document" and "valid, but against house style". - Step 6.
Read the exit code of the failing run.
echo $?1A warning alone leaves the exit code at 0, so a spec can carry warnings and still pass the gate.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| A timing line with no rows, exit 0 | The document matches the OpenAPI structure | Nothing. Keep the command in the pull request check. |
| error struct | A required field is absent from an object | Fix it first. Generators and parsers refuse the file in this state. |
| error no-unresolved-refs | A $ref points at a target that is not there | Check the spelling, then check whether a split file was left out. |
| warning no-unused-components | A schema is defined and never referenced | Usually a typo in a pointer. Read it together with the row above. |
| error path-parameters-defined | The path template names a parameter the operation never declares | Add the parameter. Clients send the path, servers cannot bind the value. |
| Rows present, exit 0 | Every row was a warning | Decide whether the rule belongs in the gate, not whether to ignore it. |
Common mistakes
What to check next
- How to validate JSON against schema: take a schema out of
components/schemasand run a real payload through it. - How to check content-type of API response: confirm the running service answers with the media types the spec declares.
- How to check API versioning: the spec carries a version, and the deployed service may not agree.
- How to test API error responses: specs describe the 200 well and the 4xx rarely at all.
- Api testing checklist: where this check sits in a release pass.
FAQ
How to validate a Swagger schema?
Swagger 2.0 files lint with the same command. Redocly CLI reads swagger: '2.0' and applies the rules for that version. Converting to OpenAPI 3.x first is a separate change with its own risk, so validate the file as it is before converting it.
Can I validate an OpenAPI spec online?
Editors such as Swagger Editor parse and report as you type, which suits drafting. They do not run in CI and they do not pin a ruleset, so a file that passes in a browser can fail the build. Use the CLI for the gate.
Does a valid spec mean the API matches it?
No. Linting reads one file and never sends a request. Pair it with a contract test that captures a real response and validates it against the schema the spec declares for that route.
Which problems should fail the build?
Start with errors from the minimal ruleset, which is struct plus unresolved references. Add style rules one at a time, after the existing spec passes them, so the gate never lands on the team as a wall of pre-existing warnings.
Verified
Verified by Maks Verny@redocly/cli 2.52.1node 22.23.2
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
intermediate6 minpublished updated Maks Verny