Linting

Lint Rules Reference

Applies to
Gatewayruntime for API, AI, and MCP gateways
Hosting
Managed EdgeManaged DedicatedSelf-Hosted

Each rule has an ID, a default severity, and the presets that include it. Use the ID to change the rule's severity, limit it to some routes, or turn it off in zuplo.lint.jsonc. To add rules of your own, see Custom lint rules.

Rules by preset

RuleSeverityrecommendedstrictsecurityAuto-fix
operation-operationIderror✓✓✓✓
operation-descriptionwarn✓✓✓
operation-tagswarn✓✓✓
path-paramserror✓✓✓
no-ambiguous-pathserror✓✓✓
zuplo/no-undefined-policieserror✓✓✓
zuplo/no-empty-inbound-policieswarn✓✓✓✓
zuplo/require-route-labelwarn✓✓✓
zuplo/schema-policieserror✓✓✓
zuplo/schema-policy-optionswarn✓✓✓
zuplo/schema-x-zuplo-routeerror✓✓✓
zuplo/require-policyper policy✓✓
zuplo/require-cors-policyerror✓
info-descriptionwarn✓
info-contactwarn✓
no-http-verbs-in-patherror✓

Two more IDs can appear in results without being rules you configure:

  • breaking-change — reported by breaking change detection.
  • lint-execution-error — a rule failed to run against a file. This usually means the file isn't valid JSON or isn't valid OpenAPI, so it has no line number (0:0). Fix the file and run the linter again.

General OpenAPI rules

The Zuplo presets also include these general OpenAPI rules. Each one catches a real defect and stays quiet on a typical Zuplo project.

RuleSeverityPresetsReports
duplicate-pathserrorallThe same path defined twice; only the last definition is kept.
operation-operationId-uniqueerrorallTwo operations with the same operationId.
operation-parameterserrorallThe same parameter declared twice on an operation.
path-declarations-must-existerrorallAn empty path parameter, such as /users/{}.
path-not-include-queryerrorallA query string in a path key.
oas3-operation-security-definederrorallA security entry with no matching securitySchemes definition.
oas-schema-checkerrorallA schema with an invalid type, such as "string ".
no-eval-in-markdownerroralleval() in a Markdown description.
no-script-tags-in-markdownerrorall<script> in a Markdown description.
operation-operationId-valid-in-urlwarnallAn operationId with characters that aren't URL-friendly.
operation-success-responsewarnallAn operation without a 2xx or 3xx response.
oas3-valid-schema-examplewarnallAn example that doesn't match its schema.
typed-enumwarnallAn enum value that doesn't match the enum's type.
duplicated-entry-in-enumwarnallA repeated enum value.
nullable-enum-contains-nullwarnallA nullable enum without null in its values.
path-keys-no-trailing-slashwarnallA path ending in /.
no-request-bodywarnallA request body on a GET or DELETE operation.
operation-4xx-responsewarnstrictAn operation without a 4xx error response.
oas3-parameter-descriptionwarnstrictA parameter without a description.
component-descriptionwarnstrictA component without a description.
operation-tag-definedwarnstrictAn operation tag missing from the top-level tags list.
oas3-unused-componentwarnstrictA component nothing references.
required-fields-definedwarnstrictA required property that isn't declared in properties.
no-unnecessary-combinatorwarnstrictAn allOf, anyOf, or oneOf with a single item.

These rules come from vacuum, the engine behind zuplo lint. To use vacuum's other general OpenAPI rules, or its OWASP security rules, extend vacuum:oas or spectral:owasp; see extends. Run npx zuplo lint --list-rules to see every rule and which rulesets include it.

OpenAPI rules

These rules check the OpenAPI document itself. They apply to every operation, whether or not it has an x-zuplo-route block.

operation-operationId

Every operation must have an operationId. Zuplo uses it to identify the route, and code generators and the Developer Portal use it to name operations.

Code
6:14 error Every operation must have an operationId: `operationId` must be set operation-operationId

Fix: Add a unique operationId to the operation, or run zuplo lint --fix to generate one from the method and path:

JSONCode
"/todos/{id}": { "delete": { "operationId": "deleteTodosById",

operation-description

Operations should have a description. The Developer Portal shows it as the operation's documentation.

Code
6:14 warn Operations should have descriptions: `description` must be set operation-description

Fix: Add a description that explains what the operation does.

operation-tags

Operations should have at least one tag. The Developer Portal groups operations by tag.

Code
6:14 warn Operations should have tags: `tags` must be set operation-tags

Fix: Add a tags array, for example "tags": ["Todos"].

path-params

Every parameter in a path template must be declared, either on the path or on the operation. The rule also reports two paths that differ only in the names of their parameters.

Code
19:5 error parameter named `id` must be defined as part of the path `/todos/{id}` definition, or in the `DELETE` operation(s) path-params

Fix: Declare the parameter with "in": "path" and "required": true:

JSONCode
"/todos/{id}": { "delete": { "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ],

no-ambiguous-paths

Two paths can't match the same request. /users/{id} and /users/{name} are ambiguous: a request to /users/42 matches both.

Code
6:5 error paths are ambiguous with one another: `/users/{id}` (GET) and `/users/{name}` (GET) no-ambiguous-paths

This pair is also reported by path-params, so turning off one of the two rules doesn't silence it.

Fix: Merge the two paths into one, or change one so it no longer overlaps, for example /users/by-name/{name}.

info-description

zuplo:strict only. The document's info object must have a description.

Code
3:11 warn API info must have a description: `description` must be set info-description

Fix: Add info.description.

info-contact

zuplo:strict only. The document's info object should have a contact.

Code
3:11 warn API info should have contact info: `contact` must be set info-contact

Fix: Add info.contact, for example "contact": { "name": "API Support", "email": "api@example.com" }.

no-http-verbs-in-path

zuplo:strict only. Path segments shouldn't be HTTP methods. The HTTP method already says what the operation does.

Code
7:19 error path `/users/get` contains an HTTP Verb `get` no-http-verbs-in-path

The rule matches whole segments, so /users/get and /post are reported, while /getUsers and /delete-user aren't.

Fix: Move the action into the method: GET /users instead of GET /users/get.

Zuplo route rules

These rules check the x-zuplo-route block of each operation. Operations without an x-zuplo-route block are skipped.

zuplo/no-undefined-policies

Every policy name in a route's policies.inbound or policies.outbound list must be defined in config/policies.json. A missing policy fails the build.

Code
15:56 error Policy "rate-limit-inbound" is referenced in route but not defined in policies.json zuplo/no-undefined-policies

Fix: Correct the spelling of the policy name in the route, or add the policy to config/policies.json. The rule only runs when config/policies.json defines at least one policy.

zuplo/no-empty-inbound-policies

A route shouldn't have an empty policies.inbound array. An empty array has the same effect as no array, and it's usually left behind after removing a policy.

Code
31:36 warn Route delete /todos/{id} has an empty inbound policies array zuplo/no-empty-inbound-policies

Fix: Remove the empty array, or run zuplo lint --fix.

zuplo/require-route-label

Routes should have a label in x-zuplo-route. The label names the route in the Zuplo Portal's route designer, logs, and analytics.

Code
6:14 warn Route get /todos should have a label in x-zuplo-route zuplo/require-route-label

Fix: Add "label": "List todos" to the x-zuplo-route block.

zuplo/require-policy

zuplo:strict and zuplo:security, or any preset with requiredPolicies. Every route must include a policy of each required type.

PresetAuthenticationRate limiting
zuplo:stricterrorwarn
zuplo:securityerrorerror
Code
27:14 error Routes must have an authentication policy: get /health zuplo/require-policy 9:14 warn Routes should have a rate limiting policy: get /users zuplo/require-policy-warn

Requirements at warning severity report as zuplo/require-policy-warn. Configure both through zuplo/require-policy: setting it in rules or overrides applies to every requirement. zuplo/require-policy-warn isn't a rule ID you can configure.

Fix: Add an authentication policy, such as API key authentication, or a rate limiting policy to the route's policies.inbound list. For routes that are intentionally public, such as a health check, turn the rule off for them with an override:

JSONCode
{ "version": 2, "overrides": [ { "paths": ["/health"], "rules": { "zuplo/require-policy": "off" } }, ], }

To change which policy types count, set your own requiredPolicies. To require a policy on only some routes, use a custom rule.

zuplo/require-cors-policy

zuplo:strict only. Every route must set corsPolicy in x-zuplo-route, so the CORS behavior of each route is a deliberate choice.

Code
62:14 error Route get /no-cors should have a corsPolicy in x-zuplo-route zuplo/require-cors-policy

Fix: Set corsPolicy to the name of a custom CORS policy, to "anything-goes", or to "none". Any value satisfies the rule, including "none".

Schema rules

These rules validate your configuration against the same JSON schemas Zuplo uses at build time. They're part of every preset.

zuplo/schema-policies

Validates the structure of config/policies.json: each policy has a name, policyType, and handler, and each name uses only lowercase letters, numbers, and dashes.

Code
4:7 error Schema validation error at /policies/0/name: at '/policies/0/name': 'INVALID_NAME' does not match pattern '^[0-9a-z-]+$' zuplo/schema-policies 15:6 error Schema validation error at /policies/1: at '/policies/1': missing property 'handler' zuplo/schema-policies

Fix: Correct the field the message points to. The path after Schema validation error at is the location in the file, so /policies/0/name is the name of the first policy.

zuplo/schema-policy-options

Validates handler.options of each built-in policy against that policy's options. This catches missing required options, wrong types, and values outside an allowed list. The rule reports warnings by default, so these results don't fail a run unless you set failOn to "warn" or raise the rule to "error".

Code
17:44 warn Schema validation error at /policies/1/handler/options: at '/policies/1/handler/options': missing properties 'rateLimitBy', 'requestsAllowed', 'timeWindowMinutes' zuplo/schema-policy-options 10:11 warn Schema validation error at /policies/0/handler/options/rateLimitBy: at '/policies/0/handler/options/rateLimitBy': value must be one of 'user', 'ip', 'function', 'all' zuplo/schema-policy-options

Custom code policies and policy types the linter doesn't recognize are skipped, so they never produce results from this rule.

Fix: Compare the options with the policy's page in the policy catalog.

zuplo/schema-x-zuplo-route

Validates each x-zuplo-route block in your OpenAPI files: it must have a handler, policy names must use valid characters, and it can't contain properties Zuplo doesn't recognize.

Code
12:26 error Schema validation error at : at '': additional properties 'unknownProperty' not allowed zuplo/schema-x-zuplo-route 30:26 error Schema validation error at : at '': missing property 'handler' zuplo/schema-x-zuplo-route

An empty location (at '') means the problem is in the x-zuplo-route object itself.

Fix: Remove or rename the unrecognized property, or add the missing one. A misspelled property such as corsPolicies shows up here as an additional property. See x-zuplo-route for the properties it accepts.

Next steps

Last modified on