Diagnostics
Every code defineApi, the OpenAPI, AsyncAPI and Arazzo renderers and their checks report, with its severity, its cause and how to fix it.
Building and rendering an API document never fails silently. Each finding
is a SpecDiagnostic:
interface SpecDiagnostic {
code: string;
severity: "error" | "warning" | "info";
message: string;
pointer?: string; // a JSON Pointer into the model or the document
}defineApi(...).diagnostics holds the findings from building the model, and
every render result's diagnostics adds the ones from that render. The
list has no duplicates. createOpenApi, createAsyncApi and createArazzo
throw on any error and pass the rest to onDiagnostic. Match on code; the message text can change.
The overlay codes are on the overlay page.
Building the model
| Code | Severity | Cause | Fix |
|---|---|---|---|
component-conflict | error | Two different schemas got the same component name; the first one is kept | Give one of them another $id, or rename it in componentName |
operation-id-duplicate | error | Two operations have the same id | Set operationId on the route, or change the operationId option |
permission-unresolved | error | The authorizer's key() threw for a permission, or a permission that isn't a string has no authorizer | Pass the authorizer to defineApi, or fix the reference |
permission-missing | error | With requirePermissions: true, a write declares no permission | Add a permission to the route or action, or permissions to the resource |
schema-unconvertible | warning | A Standard Schema has no Standard JSON Schema, so the document accepts any value there | Use a schema library that implements Standard JSON Schema, such as zod 4 or ArkType |
extend-unknown | warning | A resource's extend names no operation or action of it | Fix the key, or use * for every operation |
plugin-describe-failed | warning | A plugin's describe hook threw; the rest of the model is built | Fix the plugin |
Rendering
| Code | Severity | Cause | Fix |
|---|---|---|---|
preview-version | warning | The version is a preview (3.3-preview), whose fields can change | Render a stable version for anything you publish |
preview-only | info | A Security Profiles scheme is left out of a version before 3.3, with the requirements that name it | Expected; render 3.3-preview to see it |
method-unsupported | warning | A query operation is left out of 3.0 or 3.1 | Render 3.2, or add a GET or POST route for old clients |
webhooks-unsupported | warning | 3.0 has no webhooks, so they are left out | Render 3.1 or later |
fragment-conflict | warning | A fragment replaced a generated value; the pointer says which | Expected when you meant to override it; otherwise use extend or fix the fragment |
Checks on the finished document
These run last, after transform and overlays, so they also catch what
those broke.
| Code | Severity | Cause |
|---|---|---|
operation-id-duplicate | error | Two operations in the document share an operationId |
security-unresolved | error | A security requirement names a scheme the document doesn't have |
path-param-unknown | error | A path parameter is declared but isn't in the path template |
path-param-missing | error | A {name} in the path template has no parameter |
ref-unresolved | error | A local $ref (one that starts with #) points nowhere |
AsyncAPI
asyncapiFormat reports these while it renders
the events, channels, messages and webhooks of the model.
| Code | Severity | Cause | Fix |
|---|---|---|---|
asyncapi-table-unknown | error | events.tables names a table that isn't in realtime.tables, or cloudEvents.rows an unknown table | Fix the key, or add the table to realtime.tables |
asyncapi-realtime-url-invalid | error | realtimeUrl isn't an absolute URL | Pass the full websocket URL, such as wss://<ref>.supabase.co/realtime/v1/websocket |
asyncapi-message-unknown | error | A channel lists a message id that no message has; it is left out | Add the message to messages, or fix the id |
asyncapi-operation-message-unknown | error | An operation lists a message that isn't one of its channel's | List only messages of the operation's channel |
asyncapi-operation-id-duplicate | error | Two operations have the same id; the first one is kept | Rename one of the channel operations or topics |
asyncapi-channel-id-duplicate | error | Two channels have the same id after invalid characters become _; the first one is kept | Give one of the channels another id |
asyncapi-server-missing | warning | The document has websocket channels but no realtimeUrl | Set realtimeUrl |
asyncapi-schema-opaque | warning | A topic event or block event schema has no Standard JSON Schema, so its payload is left open | Use a schema library that implements Standard JSON Schema, or pass true |
asyncapi-schema-dialect | warning | A message payload or headers use JSON Schema 2020-12 keywords the draft-07 Schema Object lacks | Expected when your tools read 2020-12; otherwise rewrite the schema without them |
asyncapi-emits-unknown | warning | An action's emits names an event no message describes | Fix the name, or describe the event in events or messages |
asyncapi-topic-open | info | A topic lists no events, so it has one open message | Add events to document the payloads |
asyncapi-key-invalid | info | A channel, message or operation id has characters AsyncAPI keys don't allow; it is written with _ | Expected; use letters, digits, _ and - to keep the id |
asyncapi-security-unsupported | info | A Security Profiles scheme is left out, because AsyncAPI can't describe it | Expected |
asyncapi-security-combined | info | A requirement that needs several schemes together is listed as alternatives | Expected; AsyncAPI security lists have no AND |
These checks run on the finished document, after transform and overlays:
| Code | Severity | Cause |
|---|---|---|
asyncapi-ref-unresolved | error | A local $ref points nowhere |
asyncapi-key-invalid | error | A channel id has characters AsyncAPI keys don't allow |
asyncapi-parameter-unknown | error | A channel parameter isn't in the channel's address |
asyncapi-parameter-missing | error | A {name} in a channel's address has no parameter |
asyncapi-operation-channel-unknown | error | An operation doesn't reference a channel under #/channels/ |
asyncapi-operation-message-unknown | error | An operation lists a message outside its channel |
Arazzo
arazzoFormat reports these while it renders the
model's workflows.
| Code | Severity | Cause | Fix |
|---|---|---|---|
arazzo-workflows-empty | error | The model has no workflows, and an Arazzo document needs one | Pass workflows, or a built-in such as paginationWorkflows() |
arazzo-operation-unknown | error | A step calls an operationId the API doesn't have | Fix the id, or serve the operation; type it with ResourceOperationIds |
arazzo-parameter-in-missing | error | A parameter has no in, and the operation has no parameter by that name | Set in, or fix the parameter name |
arazzo-channel-step-unsupported | error | A step uses an AsyncAPI channel in Arazzo 1.0; the step is left out | Render version 1.1 |
arazzo-channel-unknown | error | A channel step names a channel the model doesn't have | Fix the id, or add the channel through events or channels |
arazzo-action-parameters-unsupported | warning | A success or failure action has parameters in Arazzo 1.0; they are left out | Render version 1.1 |
These checks run on the finished document, after transform and overlays:
| Code | Severity | Cause |
|---|---|---|
arazzo-id-invalid | error | A workflow or step id has characters other than letters, digits, _ and - |
arazzo-workflow-id-duplicate | error | Two workflows have the same id |
arazzo-step-id-duplicate | error | A workflow has two steps with the same id |
arazzo-step-target | error | A step has none, or more than one, of operationId, operationPath, channelPath and workflowId |
arazzo-workflow-unknown | error | A step calls a workflow the document doesn't have |
arazzo-step-output-unknown | error | A $steps.<step>.outputs.<name> expression names an output no such step declares |
arazzo-step-output-order | error | A step reads the output of itself or of a later step |
arazzo-source-unknown | error | A $sourceDescriptions.<name> expression names a source the document doesn't list |
arazzo-goto-unknown | error | A goto or retry action targets a step or workflow that doesn't exist |
arazzo-input-undeclared | warning | An $inputs.<name> expression names a property the workflow's inputs don't declare |
Fail a build on errors
const { document, diagnostics } = api.openapi({ version: "3.1" });
const errors = diagnostics.filter(
(diagnostic) => diagnostic.severity === "error",
);
if (errors.length > 0) {
for (const error of errors)
console.error(`${error.code}: ${error.message} ${error.pointer ?? ""}`);
process.exit(1);
}Last updated on