Skip to content

[BUG] The generated OpenAPI document asserts what the gateway does not honour #583

Description

@bburda

Summary

The generated OpenAPI document asserts a great deal that the gateway does not honour, and omits a great deal that it does. An audit across every functional group found the same root cause repeatedly: the generator does not read something the code already knows, so facts about a route are declared separately from the handler that implements them and drift apart silently.

Representative findings, all measured against a served document rather than inferred:

  • 45 operations declared a 2xx they cannot return. Status was attached by hand at the registration while the handler's return type said otherwise.
  • 28 routes could answer 501 and none declared it. Feature gates refuse a request without the document mentioning the refusal.
  • 15 schemas were unreachable from any operation, and 9 operations published a body with no content at all.
  • 12 responses used format: binary, which is not valid in OpenAPI 3.1.
  • Location was documented on created resources but not published, and on nine routes it was emitted as a URI that 404s when the request carried a trailing slash.
  • The document advertised entity resource collections the gateway does not serve and omitted several it does.
  • Every <entity-path>/docs sub-document shipped dangling $refs — a client generating code from one got references to nothing.
  • Published parameter bounds were not enforced on every route that published them.

The pattern is not that anyone was careless. It is that a fact stated in two places will diverge, and nothing was checking.

Proposed solution

Derive what can be derived, and where it cannot be derived, declare it once at a seam that also does the work.

Concretely: the success status comes from the handler's return type rather than a hand-attached number; feature gates declare the status they return; the typed route registry becomes the single source for paths, parameters, response headers and error sets; the RBAC permission table is generated from the route registrations rather than maintained as a literal; and the /docs sub-documents are projected from the real document rather than rebuilt by hand.

Where a fact genuinely cannot be derived — a hand-maintained set, a prose description — it gets a mechanical check, and where no check is possible the claim is written narrowly rather than universally.

The enforcement mechanism matters as much as the fix: a contract test that walks the served document, a build-time recorder that asserts every status the gateway emits is declared, and a startup completeness check. Without those, the same drift returns.

Additional context

The work also surfaced defects that were not documentation problems and are being fixed alongside:

  • A downstream validation refusal was reported to clients as a 503 server error, because the classification matched a substring of the error message.
  • fault_code was published with a maximum of 256 while the fault manager enforced 128, so values in between produced a server error.
  • Raising that bound exposed a silent failure: a long fault code pushed the rosbag directory name past NAME_MAX, the exception was caught and only logged, and the bag was never written.
  • Any 401/403/429 answered before the request body was read stranded the payload and poisoned the keep-alive connection, so the next request on it received a spurious 400.

Enforcement of authorization is deliberately not moved as part of this work — only the origin of the permission table changes. Routes registered outside the registry (docs, Swagger UI, plugins) would lose their rule if enforcement followed the table.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions