Skip to content

docs(openapi): Autofix OpenAPI spec validation errors#2517

Draft
Pijukatel wants to merge 4 commits into
masterfrom
claude/fix-openapi-errors-6ZW0S
Draft

docs(openapi): Autofix OpenAPI spec validation errors#2517
Pijukatel wants to merge 4 commits into
masterfrom
claude/fix-openapi-errors-6ZW0S

Conversation

@Pijukatel
Copy link
Copy Markdown
Contributor

@Pijukatel Pijukatel commented May 10, 2026

Summary

Autogenerated OpenAPI fixes suggestions based on validation errors generated from running API integration tests with OpenAPI validator turned on.

Error log: https://apify-pr-test-env-logs.s3.us-east-1.amazonaws.com/apify/apify-core/27741/api-56afaaf4cd4c0b4255dbf6603b799eeb96d3a5d8.log

apify-core version: https://github.com/apify/apify-core/commit/19160bcbc2da9e8e242227fea757c953a37ee797

Stop reason: Iteration in progress; pending verification of the third commit (refactoring of existing actor-runs and acts run-action paths into the new shared components).

Detailed changes description

Error fixes

Add full-permission-actor-not-approved to ErrorType enum

  • Files: apify-api/openapi/components/schemas/common/ErrorType.yaml:129
  • Error: Response OpenAPI validation error {"url":"/v2/acts/{actorId}/runs","method":"POST","statusCode":403,"errors":[{"message":"must be equal to one of the allowed values: ...","errorCode":"enum.openapi.validation","path":"/response/error/type"}]}
  • Root cause: The API returns error type full-permission-actor-not-approved (HTTP 403) when an Actor requires full account access but the user has not approved the permissions, but the type was missing from the ErrorType enum used by all error responses.
  • Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/packages/errors/src/errors/actor.ts#L61

Add run-input-body-not-valid-json to ErrorType enum

  • Files: apify-api/openapi/components/schemas/common/ErrorType.yaml:301
  • Error: Response OpenAPI validation error {"url":"/v2/schedules","method":"POST","statusCode":400,"errors":[{"message":"must be equal to one of the allowed values: ...","errorCode":"enum.openapi.validation","path":"/response/error/type"}]}
  • Root cause: The API returns error type run-input-body-not-valid-json (HTTP 400) when a schedule's run input body contains invalid JSON, but the type was missing from the ErrorType enum used by all error responses.
  • Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/packages/errors/src/errors/scheduling.ts#L40

Broaden 403 response of GET /v2/acts/{actorId}/builds/default

  • Files: apify-api/openapi/paths/actors/acts@{actorId}@builds@default.yaml:28
  • Error: Response OpenAPI validation error {"url":"/v2/acts/{actorId}/builds/default","method":"GET","statusCode":403,"errors":[{"message":"must be equal to constant","errorCode":"const.openapi.validation","path":"/response/error/type"}]}
  • Root cause: The 403 response schema only allowed the unknown-build-tag error type as a const, but the endpoint can also return insufficient-permissions from IAM access checks plus build-not-found, build-outdated and invalid-build from the build lookup. Replaced the narrow custom schema with the standard Forbidden response that uses the full ErrorType enum, so all valid error types for this endpoint validate correctly.
  • Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/routes/actors/build_default.ts#L30

Document POST /v2/acts/{actorId}/runs/last/abort and POST /v2/actor-tasks/{actorTaskId}/runs/last/abort

  • Files: apify-api/openapi/components/objects/actor-runs/abort.yaml, apify-api/openapi/paths/actors/acts@{actorId}@runs@last@abort.yaml, apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@runs@last@abort.yaml, apify-api/openapi/openapi.yaml:550,583
  • Error: Request OpenAPI validation error {"url":"/v2/actor-tasks/{actorTaskId}/runs/last/abort","method":"POST","errors":[{"message":"not found","path":"/actor-runs/{runId}/abort/"}]}
  • Root cause: The runs/last/abort endpoint was undocumented for both Actors and Actor tasks. The API rewrites runs/last/{action} via middleware to /v2/actor-runs/{runId}/{action}/, so the validator did not match either the original URL or the rewritten one. Added documentation following the shared-component pattern from components/objects/logs/log.yaml (YAML anchor for shared response shape, named operations referenced from each path file).
  • Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/lib/controllers.ts#L38

Document POST /v2/acts/{actorId}/runs/last/metamorph and POST /v2/actor-tasks/{actorTaskId}/runs/last/metamorph

  • Files: apify-api/openapi/components/objects/actor-runs/metamorph.yaml, apify-api/openapi/paths/actors/acts@{actorId}@runs@last@metamorph.yaml, apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@runs@last@metamorph.yaml, apify-api/openapi/openapi.yaml:552,585
  • Error: Request OpenAPI validation error {"url":"/v2/actor-tasks/{actorTaskId}/runs/last/metamorph","method":"POST","errors":[{"message":"not found","path":"/actor-runs/{runId}/metamorph/"}]}
  • Root cause: The runs/last/metamorph endpoint was undocumented for both Actors and Actor tasks. Same middleware-rewrite mechanism as abort. Added documentation using the same shared-component pattern.
  • Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/lib/controllers.ts#L39

Document POST /v2/acts/{actorId}/runs/last/reboot and POST /v2/actor-tasks/{actorTaskId}/runs/last/reboot

  • Files: apify-api/openapi/components/objects/actor-runs/reboot.yaml, apify-api/openapi/paths/actors/acts@{actorId}@runs@last@reboot.yaml, apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@runs@last@reboot.yaml, apify-api/openapi/openapi.yaml:554,587
  • Error: Request OpenAPI validation error {"url":"/v2/acts/{actorId}/runs/last/reboot","method":"POST","errors":[{"message":"not found","path":"/actor-runs/{runId}/reboot/"}]}
  • Root cause: The runs/last/reboot endpoint was undocumented for both Actors and Actor tasks. Same middleware-rewrite mechanism as abort. Added documentation using the same shared-component pattern.
  • Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/lib/controllers.ts#L40

Refactoring

  1. Introduced apify-api/openapi/components/objects/actor-runs/{abort,metamorph,reboot,resurrect,charge}.yaml shared-component files. Each file uses a YAML anchor (e.g. &sharedAbort) for the common response shape and exposes named operations (byRunId, byActorRunId, lastRunByActor, lastRunByActorTask) reused via <<: *anchor. charge only has byRunId because /v2/actor-runs/{runId}/charge is the only path the API exposes for charging.
  2. Refactored 8 existing path files (actor-runs@{runId}@{abort,metamorph,reboot,resurrect,charge}.yaml, acts@{actorId}@runs@{runId}@{abort,metamorph,resurrect}.yaml) into thin $ref wrappers pointing at the shared operations. Net diff: 278 lines of duplication removed (437 lines deleted, 159 lines added across the refactor commit). Bundled spec output is functionally unchanged for all 13 affected operations.
  3. Registered the 6 new last-run convenience tags (Last Actor run's {abort,metamorph,reboot}, Last Actor task run's {abort,metamorph,reboot}) in apify-api/openapi/components/tags.yaml and listed them in apify-api/openapi/components/x-tag-groups.yaml under the existing Convenience endpoints group, alongside the analogous log/default dataset/default key-value store/default request queue tags.

actor-tasks was reviewed for the same refactoring strategy: there are no /v2/actor-tasks/{actorTaskId}/runs/{runId}/{action} direct endpoints in apify-core/src/api/src/routes/routes_config.ts (only runs/last* via the lastRunRouter), so no further refactor is needed there.

Unfixed errors

False positives

The following errors were not fixed because they are known false positives of the validator that incorrectly raises errors when a string, format: date-time field is allowed to be null (documented in test-OpenAPI-autofixes/CLAUDE.md).

Nullable taggedBuilds[*].finishedAt

Nullable lastDispatch.finishedAt (webhooks)

Nullable nextRunAt and lastRunAt (schedules)

Nullable finishedAt (build abort and run abort)

  • Error: Response OpenAPI validation error {"url":"/v2/actor-builds/{buildId}/abort","method":"POST","statusCode":200,"errors":[{"message":"must be string,null","errorCode":"type.openapi.validation","path":"/response/data/finishedAt"}]} and Response OpenAPI validation error {"url":"/v2/actor-tasks/{actorTaskId}/runs/last/abort","method":"POST","statusCode":200,"errors":[{"message":"must be string,null","errorCode":"type.openapi.validation","path":"/response/data/finishedAt"}]}
  • Root cause: Same nullable date-time false positive. The run-abort case for runs/last/abort only surfaced after the endpoint was added in this PR — previously the validator could not match the URL at all and skipped response validation; now it validates and reports the same false positive that already affects the actor-builds/{buildId}/abort endpoint.
  • Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/packages/types/src/actor.ts#L1160

Nullable calls[*].startedAt and calls[*].finishedAt (webhook dispatches)

Out of scope errors

method query parameter validation noise

  • Error: Request OpenAPI validation error {"url":"/v2/...?...&method=...",...,"errors":[{"message":"Unknown query parameter 'method'","path":"/query/method"}]}
  • Root cause: The API supports a global method query parameter for HTTP method override (documented in the spec description and implemented as middleware), but it is not declared as a parameter on individual paths. Adding a global parameter to all paths is a structural refactor outside the scope of a per-error fix.

Tests that intentionally send malformed requests

  • Error: Request OpenAPI validation error for endpoints where tests deliberately send invalid generalAccess, malformed handledAt, missing required fields, unsupported Content-Type, invalid filter values, invalid event types, OPTIONS method, etc.
  • Root cause: These integration tests verify error handling; the spec is correct.

Issues

Partially implements: #2286

Error: Response OpenAPI validation error {"url":"/v2/acts/{actorId}/runs","method":"POST","statusCode":403,"errors":[{"message":"must be equal to one of the allowed values: ...","errorCode":"enum.openapi.validation","path":"/response/error/type"}]}
Files: apify-api/openapi/components/schemas/common/ErrorType.yaml:129
Root cause: Error type "full-permission-actor-not-approved" is returned by the API for actors that require full account access but were not yet approved (HTTP 403), but the type was missing from the ErrorType enum.
Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/packages/errors/src/errors/actor.ts#L61

Error: Response OpenAPI validation error {"url":"/v2/schedules","method":"POST","statusCode":400,"errors":[{"message":"must be equal to one of the allowed values: ...","errorCode":"enum.openapi.validation","path":"/response/error/type"}]}
Files: apify-api/openapi/components/schemas/common/ErrorType.yaml:301
Root cause: Error type "run-input-body-not-valid-json" is returned by the API when the schedule run input body contains invalid JSON (HTTP 400), but the type was missing from the ErrorType enum.
Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/packages/errors/src/errors/scheduling.ts#L40

Error: Response OpenAPI validation error {"url":"/v2/acts/{actorId}/builds/default","method":"GET","statusCode":403,"errors":[{"message":"must be equal to constant","errorCode":"const.openapi.validation","path":"/response/error/type"}]}
Files: apify-api/openapi/paths/actors/acts@{actorId}@builds@default.yaml:28
Root cause: The 403 response schema only allowed "unknown-build-tag" as a const, but the endpoint can also return "insufficient-permissions" from IAM access checks plus "build-not-found", "build-outdated" and "invalid-build" from the build lookup. Replaced the narrow custom schema with the standard Forbidden response that uses the full ErrorType enum.
Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/routes/actors/build_default.ts#L30
@github-actions github-actions Bot added this to the 140th sprint - Tooling team milestone May 10, 2026
@github-actions github-actions Bot added the t-tooling Issues with this label are in the ownership of the tooling team. label May 10, 2026
claude added 3 commits May 10, 2026 04:35
Error: Request OpenAPI validation error {"url":"/v2/actor-tasks/{actorTaskId}/runs/last/abort","method":"POST","errors":[{"message":"not found","path":"/actor-runs/{runId}/abort/"}]}
Files: apify-api/openapi/components/objects/actor-runs/abort.yaml, apify-api/openapi/paths/actors/acts@{actorId}@Runs@last@abort.yaml, apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@Runs@last@abort.yaml, apify-api/openapi/openapi.yaml
Root cause: The runs/last/abort endpoint was missing from the OpenAPI spec for both Actors and Actor tasks. The API rewrites runs/last/{action} to /v2/actor-runs/{runId}/{action}/ via middleware, so the validator did not match either the original URL or the rewritten one.
Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/lib/controllers.ts#L38

Error: Request OpenAPI validation error {"url":"/v2/actor-tasks/{actorTaskId}/runs/last/metamorph","method":"POST","errors":[{"message":"not found","path":"/actor-runs/{runId}/metamorph/"}]}
Files: apify-api/openapi/components/objects/actor-runs/metamorph.yaml, apify-api/openapi/paths/actors/acts@{actorId}@Runs@last@metamorph.yaml, apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@Runs@last@metamorph.yaml, apify-api/openapi/openapi.yaml
Root cause: The runs/last/metamorph endpoint was missing from the OpenAPI spec for both Actors and Actor tasks. The API rewrites runs/last/{action} to /v2/actor-runs/{runId}/{action}/ via middleware, so the validator did not match either the original URL or the rewritten one.
Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/lib/controllers.ts#L39

Error: Request OpenAPI validation error {"url":"/v2/acts/{actorId}/runs/last/reboot","method":"POST","errors":[{"message":"not found","path":"/actor-runs/{runId}/reboot/"}]}
Files: apify-api/openapi/components/objects/actor-runs/reboot.yaml, apify-api/openapi/paths/actors/acts@{actorId}@Runs@last@reboot.yaml, apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@Runs@last@reboot.yaml, apify-api/openapi/openapi.yaml
Root cause: The runs/last/reboot endpoint was missing from the OpenAPI spec for both Actors and Actor tasks. The API rewrites runs/last/{action} to /v2/actor-runs/{runId}/{action}/ via middleware, so the validator did not match either the original URL or the rewritten one.
Reference: https://github.com/apify/apify-core/tree/19160bcbc2da9e8e242227fea757c953a37ee797/src/api/src/lib/controllers.ts#L40

The new endpoints follow the same shared-component pattern as components/objects/logs/log.yaml: a YAML anchor for the common response shape and named operations merged via "<<: *anchor", referenced from each path file.
…t} endpoints

The previous commit introduced operation tags "Actors/Last run" and
"Actor tasks/Last run" without declaring them in components/tags.yaml,
which triggered the spectral "operation-tag-defined" warning.

Switched to the existing "Last Actor run's X" / "Last Actor task run's X"
naming convention used by all other runs/last/* convenience endpoints,
added six new top-level tag definitions in components/tags.yaml, and
listed them in components/x-tag-groups.yaml under "Convenience endpoints"
right next to the matching "log", "default dataset", "default key-value
store", and "default request queue" tags.

No spec semantics change; this only fixes the lint warning.
… components

Refactored actor-runs@{runId}@{abort,metamorph,reboot,resurrect,charge}.yaml
and acts@{actorId}@Runs@{runId}@{abort,metamorph,resurrect}.yaml path files
into thin "$ref" wrappers that point to operations defined in
components/objects/actor-runs/{abort,metamorph,reboot,resurrect,charge}.yaml.

Pattern follows the existing components/objects/logs/log.yaml example:
- Each shared file declares a "&sharedX" YAML anchor for the common
  response shape.
- Named operations ("byRunId", "byActorRunId", "lastRunByActor",
  "lastRunByActorTask") merge the anchor and add their own metadata.
- charge.yaml has only "byRunId" because /v2/actor-runs/{runId}/charge
  is the only path the API exposes for charging.

actor-tasks has no equivalent /actor-tasks/{actorTaskId}/runs/{runId}/X
endpoints in apify-core (only the runs/last router), so nothing to
refactor there.

Net change: 437 lines deleted, 159 lines added; 278 lines of duplication
removed. Bundle output is functionally unchanged for all 13 affected
operations - only one cosmetic typo fix (missing space in the deprecated
metamorph description) and one schema-shape consistency tweak (resurrect
200 now uses allOf wrapping in both variants).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants