OpenAPI errors

How to fix missing operationId errors in OpenAPI

A missing operationId doesn't break validation — the field is optional per the OpenAPI spec. What it breaks is your generated SDK: without it, generators fall back to building a method name from the HTTP verb and path segments, and you end up calling api.usersUserIdGet(...) instead of api.getUser(...). This post reproduces the fallback naming with a real generator run and shows the one-line fix. Reproduced 2026-09-21.

What does the error actually look like?

There's no hard error — that's the trap. Running OpenAPI Generator v7.25.0 against a path with no operationId produces a warning, not a failure, and then silently ships a machine-generated name into your SDK:

[main] WARN  o.o.codegen.DefaultCodegen - Empty operationId found for path: get /users/{userId}. Renamed to auto-generated operationId: usersUserIdGet

The build succeeds. usersUserIdGet becomes a real, callable method on the generated client. Nothing in CI fails unless you're specifically linting for it.

Minimal spec that triggers it

openapi: 3.0.3
info:
  title: Demo API
  version: 1.0.0
paths:
  /users/{userId}:
    get:
      summary: Get a user
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK

No operationId anywhere on the get operation. This is valid OpenAPI 3.0 and 3.1 — swagger-cli validate v4.0.4 against this file returns api.yaml is valid, exit code 0.

Why does the spec allow this?

Because operationId was designed as a convenience field, not a structural requirement. The OpenAPI 3.0 specification describes it as: "Unique string used to identify the operation... Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is RECOMMENDED to follow common programming naming conventions." Nothing there says required — and the wording hasn't changed going into 3.1.

That "MAY use" is exactly what code generators do: they use it as the method name whenever it's present, and derive one from the path and verb whenever it isn't. The spec never promises what that fallback name will look like, so it's entirely generator-specific — and generally worse than anything you'd choose by hand.

What does the generated code look like?

Running the same spec through openapi-generator-cli generate -g typescript-fetch produces this real method signature:

async usersUserIdGet(
  requestParameters: UsersUserIdGetRequest,
  initOverrides?: RequestInit | runtime.InitOverrideFunction
): Promise<UsersUserIdGet200Response> {
  // ...
}

usersUserIdGet is the path (/users/{userId}) and verb (GET) concatenated and camelCased. It's deterministic, but it reads nothing like getUser, and it will silently change if you ever rename the path — a refactor that shouldn't touch your SDK's public API now does, because the method name was never actually chosen, just derived.

Redocly CLI v2.53.3, run with redocly lint and no custom ruleset, flags the same file as a warning rather than an error:

Operation object should contain `operationId` field.

That's the operation-operationId rule — on by default in Redocly's recommended ruleset, but a warning, so it won't fail a CI gate configured to only break on errors.

How do you fix it?

Add an explicit, descriptive operationId to every operation:

# Before
paths:
  /users/{userId}:
    get:
      summary: Get a user
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK

# After
paths:
  /users/{userId}:
    get:
      operationId: getUser
      summary: Get a user
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK

That single line changes the generated method from usersUserIdGet(...) to getUser(...) — no template configuration, no operationId mapping overrides, no generator flags needed. It's the cheapest fix in this entire series of posts.

A naming convention that survives refactors

Pick a pattern and apply it everywhere, so nobody has to think about it per-endpoint: <verb><Resource> for collection actions (listUsers, createUser) and <verb><Resource>By<Field> when a path parameter disambiguates it (getUserById). Whatever convention you pick, the goal is the same one the spec text points at — the id should read as a programming identifier, because that's literally what it becomes.

How to catch this before it ships

  1. Run a semantic linter, not just a schema check — swagger-cli validate will pass a spec with zero operationIds anywhere in it, as shown above; you need redocly lint, spectral lint, or the free in-browser OpenAPI validator to see the warning.
  2. Fail CI on the warning, not just on errors — Redocly and Spectral both let you promote operation-operationId to an error in a custom ruleset.
  3. Check generated method names as part of your SDK review, not just the spec — a missing operationId is invisible in the YAML diff if you're only skimming for red flags; it shows up as an ugly name in the generated client.

If you're generating a TypeScript or Python SDK from a spec you don't fully control — an upstream vendor's OpenAPI file, or one assembled by several teams — Sourced flags missing and duplicate operationIds during the same pass that builds your hosted docs and typed SDK, before a bad method name reaches a customer's autocomplete. Start a free report.

FAQ

Is operationId required in OpenAPI?

No. It's optional in both OpenAPI 3.0 and 3.1. A spec with zero operationId fields anywhere is valid per the spec text and will pass schema-only validators like swagger-cli validate.

What happens if I don't set operationId?

Your code generator invents one from the HTTP method and path, typically in the shape <pathSegments><Verb> (for example usersUserIdGet). The exact pattern differs by generator, and it changes if you rename the path later, which can silently rename a public SDK method.

Does a missing operationId break MCP tool generation too?

Often worse than SDK generation. MCP tool names are what an LLM agent reads to decide which tool to call — see our duplicate operationId post for what happens when two operations collide on the same generated name. A generic auto-generated name gives the agent less signal to pick the right tool, even when it's technically unique.

Will a JSON Schema validator catch a missing operationId?

No. operationId uniqueness and presence are semantic rules the OpenAPI ecosystem layers on top of JSON Schema, not part of the base schema structure. You need a semantic linter — Redocly CLI, Spectral, or Sourced's validator — in the loop specifically for this.

Do I need operationId on every single operation, or just the ones I care about?

Every operation that will ever be called from generated code. If even one endpoint is missing it, that one endpoint gets an auto-generated name while its siblings look clean — an inconsistency that's easy to miss in a large spec and confusing for whoever reads the SDK later.

Can I rename operationId later without breaking clients?

Only if your generator maps method names by operationId at generation time (most do) and you regenerate and republish the SDK — existing installed versions won't retroactively rename themselves. Treat operationId as part of your public API surface once you've published a version with a given name.