Skip to content

docs(openapi): Autofix OpenAPI spec validation errors - #2785

Merged
Pijukatel merged 2 commits into
masterfrom
claude/openapi-errors-fix-xjikyy
Jul 20, 2026
Merged

docs(openapi): Autofix OpenAPI spec validation errors#2785
Pijukatel merged 2 commits into
masterfrom
claude/openapi-errors-fix-xjikyy

Conversation

@Pijukatel

@Pijukatel Pijukatel commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

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

Error log: User-provided production api.log excerpt (SOFT_FAIL response validation errors, Jul 18–20, 2026). The log is not available at a public URL, so the normalized log lines are included below:

Jul 18 00:34:05 apify-api SOFT_FAIL {"errors":[{"message":"no schema defined for status code '402' in the openapi spec","path":"/v2/actors/{actorId}/builds?version=1.0&useCache=false&waitForFinish=180"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":402.0,"url":"/v2/actors/{actorId}/builds?version=1.0&useCache=false&waitForFinish=180"}
Jul 18 02:50:38 apify-api SOFT_FAIL {"errors":[{"message":"no schema defined for status code '404' in the openapi spec","path":"/v2/actor-tasks"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":404.0,"url":"/v2/actor-tasks"}
Jul 20 08:15:38 apify-api SOFT_FAIL {"errors":[{"errorCode":"type.openapi.validation","message":"must be string","path":"/response/data/taggedBuilds/{tag}/buildNumber"},{"errorCode":"type.openapi.validation","message":"must be null","path":"/response/data/taggedBuilds/{tag}"},{"errorCode":"anyOf.openapi.validation","message":"must match a schema in anyOf","path":"/response/data/taggedBuilds/{tag}"},{"errorCode":"type.openapi.validation","message":"must be null","path":"/response/data/taggedBuilds"},{"errorCode":"anyOf.openapi.validation","message":"must match a schema in anyOf","path":"/response/data/taggedBuilds"}],"method":"GET","msg":"Response OpenAPI validation error","statusCode":200.0,"url":"/v2/actors/{actorId}"}
Jul 20 08:47:37 apify-api SOFT_FAIL {"errors":[{"message":"no schema defined for status code '409' in the openapi spec","path":"/v2/actor-tasks/{actorTaskId}"}],"method":"PUT","msg":"Response OpenAPI validation error","statusCode":409.0,"url":"/v2/actor-tasks/{actorTaskId}"}

A follow-up regression run of the integration tests with the fixed spec (validator run results) surfaced one additional response validation error, fixed as the fifth fix below.

apify-core version: https://github.com/apify/apify-core/commit/48a50ff1c30b82ade5283e5465ef63a7ec09095b

Stop reason: All 4 unique errors from the provided log are fixed, plus 1 new response validation error found in the regression run. The original 4 errors cannot be reproduced in the PR test environment (they come from production traffic and legacy data), so they were verified against apify-core source instead. The regression run with the fixed spec passed (status SUCCESS) and its api.log contains no response validation errors caused by these changes; the remaining request validation errors in that log are deliberate malformed-request tests or pre-existing issues unrelated to this diff (see Unfixed errors).

Detailed changes description

Error fixes

Add 402 (Payment Required) response to Build Actor endpoint

  • Files: apify-api/openapi/paths/actors/acts@{actorId}@builds.yaml:123
  • Error: {"errors":[{"message":"no schema defined for status code '402' in the openapi spec","path":"/v2/actors/{actorId}/builds?version=1.0&useCache=false&waitForFinish=180"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":402}
  • Root cause: POST /v2/actors/{actorId}/builds enforces the account memory and concurrent-runs limits before creating the build and throws memory-limit-exceeded/concurrent-runs-limit-exceeded errors with HTTP 402, but the spec did not document a 402 response. Reused the PaymentRequired response component, consistent with the run Actor endpoints that document 402 for the same limit checks.
  • Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/packages/actor-server/src/actor_jobs/actor_jobs.server.ts#L1269

Add 404 (Not Found) response to Create task endpoint

Allow null buildNumber in tagged builds of the Actor object

  • Files: apify-api/openapi/components/schemas/actors/TaggedBuildInfo.yaml:10
  • Error: {"errors":[{"errorCode":"type.openapi.validation","message":"must be string","path":"/response/data/taggedBuilds/{tag}/buildNumber"},{"errorCode":"anyOf.openapi.validation","message":"must match a schema in anyOf","path":"/response/data/taggedBuilds/{tag}"}],"method":"GET","msg":"Response OpenAPI validation error","statusCode":200,"url":"/v2/actors/{actorId}"}
  • Root cause: GET /v2/actors/{actorId} builds the taggedBuilds map via transformActor(), which computes buildNumber as buildOrVersionNumberIntToStr(buildNumberInt) with return type string | null; for legacy builds without a valid buildNumberInt the value is null, while the spec allowed only a string. Allowed null for buildNumber (the pattern keyword only applies to string values, so it keeps validating real build numbers).
  • Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/packages/actor/src/actors/actors.both.ts#L113

Add 409 (Conflict) response to Update task endpoint

  • Files: apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}.yaml:104
  • Error: {"errors":[{"message":"no schema defined for status code '409' in the openapi spec","path":"/v2/actor-tasks/{actorTaskId}"}],"method":"PUT","msg":"Response OpenAPI validation error","statusCode":409}
  • Root cause: PUT /v2/actor-tasks/{actorTaskId} throws actor-task-name-not-unique with HTTP 409 when renaming a task to a name already used by another task of the same user (Mongo duplicate key on userId+nameLowerCase), but the spec did not document a 409 response. Reused the Conflict response component, matching POST /v2/actor-tasks which already documents 409 for the same uniqueness constraint.
  • Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/api/src/routes/actor_tasks/actor_task.ts#L151

Add cannot-monetize-without-payout-billing-info to ErrorType enum

  • Files: apify-api/openapi/components/schemas/common/ErrorType.yaml:68
  • Error: {"url":"/v2/actors/{actorId}","method":"PUT","statusCode":400,"errors":[{"message":"must be equal to one of the allowed values: 3d-secure-auth-failed, access-right-already-exists, ...","errorCode":"enum.openapi.validation","path":"/response/error/type"}]} (from the regression run log)
  • Root cause: PUT /v2/actors/{actorId} with a paid pricing model rejects the update with HTTP 400 and error type cannot-monetize-without-payout-billing-info when the Actor owner has no payout billing info set (thrown by checkAndSanitizePricingInfosModifier), but this error type was missing from the ErrorType enum shared by all error responses.
  • Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/api/src/lib/paid_actors_helpers.ts#L364

Refactoring

None - only the minimum necessary response/schema additions were made.

Unfixed errors

Out of scope errors

Unknown query parameter standbyDeployment on run Actor endpoint

  • Error: {"url":"/v2/actors/{actorId}/runs?standbyDeployment=SINGLE_TENANT&memory=1024&build=latest","method":"POST","errors":[{"message":"Unknown query parameter 'standbyDeployment'","path":"/query/standbyDeployment"}]} (request validation, regression run log)
  • Root cause: ApifyClient 2.23.4 sends a real standbyDeployment query parameter that is not yet documented on POST /v2/actors/{actorId}/runs. Documenting it requires product input on the parameter's contract, which is beyond this autofix PR.

Undocumented endpoint GET /v2/actors/{actorId}/oauth-connections

  • Error: {"url":"/v2/actors/{actorId}/oauth-connections","method":"GET","errors":[{"message":"not found","path":"/actors/{actorId}/oauth-connections"}]} (request validation, regression run log)
  • Root cause: The endpoint exists in apify-core (src/api/src/routes/actors/oauth_connection_list.ts) but has no path in the OpenAPI spec. Adding a whole new documented endpoint is beyond this autofix PR.

Deliberate malformed-request tests and internal-only paths

  • Error: Remaining request validation errors in the regression run log: invalid input/options/generalAccess/eventTypes/filter/handledAt values, unsupported media types, invalid HTTP methods, missing required parameters, and probes of internal paths (/v2/meta/_telemetry-test, /v2/openapi.json, /v2/browser-info?method=...).
  • Root cause: Integration tests intentionally send malformed requests to verify error handling, and some internal/telemetry paths are deliberately absent from the public spec. Per the workflow rules, specs must not be changed to accommodate intentionally invalid requests.

Issues

Partially implements: #2286

…ilds buildNumber

Error: {"errors":[{"message":"no schema defined for status code '402' in the openapi spec","path":"/v2/actors/{actorId}/builds?version=1.0&useCache=false&waitForFinish=180"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":402}
Files: apify-api/openapi/paths/actors/acts@{actorId}@builds.yaml:123
Root cause: POST /v2/actors/{actorId}/builds enforces account memory and concurrent-runs limits before creating the build and throws memory-limit-exceeded/concurrent-runs-limit-exceeded errors with HTTP 402, but the spec did not document a 402 response. Reused the PaymentRequired response component, consistent with the run Actor endpoints that document 402 for the same limit checks.
Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/packages/actor-server/src/actor_jobs/actor_jobs.server.ts#L1269

Error: {"errors":[{"message":"no schema defined for status code '404' in the openapi spec","path":"/v2/actor-tasks"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":404}
Files: apify-api/openapi/paths/actor-tasks/actor-tasks.yaml:142
Root cause: POST /v2/actor-tasks looks up the Actor referenced by actId in the request payload and throws record-not-found with HTTP 404 when that Actor does not exist or is removed, but the spec did not document a 404 response. Reused the NotFound response component.
Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/api/src/routes/actor_tasks/actor_task_list.ts#L190

Error: {"errors":[{"errorCode":"type.openapi.validation","message":"must be string","path":"/response/data/taggedBuilds/{tag}/buildNumber"},{"errorCode":"anyOf.openapi.validation","message":"must match a schema in anyOf","path":"/response/data/taggedBuilds/{tag}"}],"method":"GET","msg":"Response OpenAPI validation error","statusCode":200,"url":"/v2/actors/{actorId}"}
Files: apify-api/openapi/components/schemas/actors/TaggedBuildInfo.yaml:10
Root cause: GET /v2/actors/{actorId} builds the taggedBuilds map via transformActor(), which computes buildNumber as buildOrVersionNumberIntToStr(buildNumberInt) with return type string | null; for legacy builds without a valid buildNumberInt the value is null, while the spec allowed only a string. Allowed null for buildNumber (the pattern keyword only applies to string values in JSON Schema).
Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/packages/actor/src/actors/actors.both.ts#L113

Error: {"errors":[{"message":"no schema defined for status code '409' in the openapi spec","path":"/v2/actor-tasks/{actorTaskId}"}],"method":"PUT","msg":"Response OpenAPI validation error","statusCode":409}
Files: apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}.yaml:104
Root cause: PUT /v2/actor-tasks/{actorTaskId} throws actor-task-name-not-unique with HTTP 409 when renaming a task to a name already used by another task of the same user (Mongo duplicate key on userId+nameLowerCase), but the spec did not document a 409 response. Reused the Conflict response component, matching POST /v2/actor-tasks which already documents 409 for the same uniqueness constraint.
Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/api/src/routes/actor_tasks/actor_task.ts#L151

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011VY4n4eTrXwjwT4CMsHPJD
@github-actions github-actions Bot added this to the 145th sprint - Tooling team milestone Jul 20, 2026
@github-actions github-actions Bot added the t-tooling Issues with this label are in the ownership of the tooling team. label Jul 20, 2026
@apify-service-account

apify-service-account commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

🗑️ Preview for this PR was deleted.

@apify-service-account

Copy link
Copy Markdown
Contributor

Important

Action required@Pijukatel please coordinate this docs PR with the Python API client PR linked below.

Because this PR modifies the OpenAPI specification, the generated models in apify-client-python must be regenerated to stay in sync. This has already been done automatically:

A companion PR has been opened in apify-client-python with the regenerated models: apify/apify-client-python#960

  • Please make sure to review and merge both PRs together to keep the OpenAPI spec and API clients in sync.
  • You can ask for review and help from the Tooling team if needed.

@Pijukatel Pijukatel added the adhoc Ad-hoc unplanned task added during the sprint. label Jul 20, 2026
@Pijukatel
Pijukatel requested a review from vdusek July 20, 2026 09:28
@Pijukatel
Pijukatel marked this pull request as ready for review July 20, 2026 09:28
…rType enum

Error: {"url":"/v2/actors/{actorId}","method":"PUT","statusCode":400,"errors":[{"message":"must be equal to one of the allowed values: 3d-secure-auth-failed, access-right-already-exists, ...","errorCode":"enum.openapi.validation","path":"/response/error/type"}]}
Files: apify-api/openapi/components/schemas/common/ErrorType.yaml:68
Root cause: PUT /v2/actors/{actorId} with a paid pricing model rejects the update with HTTP 400 and error type cannot-monetize-without-payout-billing-info when the owner has no payout billing info set (thrown by checkAndSanitizePricingInfosModifier), but this error type was missing from the ErrorType enum used by all error responses.
Reference: https://github.com/apify/apify-core/tree/48a50ff1c30b82ade5283e5465ef63a7ec09095b/src/api/src/lib/paid_actors_helpers.ts#L364

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011VY4n4eTrXwjwT4CMsHPJD
@apify-service-account

Copy link
Copy Markdown
Contributor

Important

Action required@Pijukatel please coordinate this docs PR with the Python API client PR linked below.

Because this PR modifies the OpenAPI specification, the generated models in apify-client-python must be regenerated to stay in sync. This has already been done automatically:

The companion apify-client-python PR has been updated with the latest spec changes: apify/apify-client-python#960

  • Please make sure to review and merge both PRs together to keep the OpenAPI spec and API clients in sync.
  • You can ask for review and help from the Tooling team if needed.

@Pijukatel
Pijukatel merged commit 7c2d71f into master Jul 20, 2026
18 checks passed
@Pijukatel
Pijukatel deleted the claude/openapi-errors-fix-xjikyy branch July 20, 2026 09:48
vdusek pushed a commit to apify/apify-client-python that referenced this pull request Jul 20, 2026
…de (#960)

- Updates the auto-generated Pydantic models and TypedDicts based on the
proposed OpenAPI specification changes.
- Based on apify-docs PR
[#2785](apify/apify-docs#2785).
Pijukatel added a commit that referenced this pull request Jul 22, 2026
## Summary
Autogenerated OpenAPI fixes suggestions based on validation errors
generated from running API integration tests with OpenAPI validator
turned on.

**Error log:** User-provided production `api.log` excerpt (SOFT_FAIL
response validation errors, Jul 16–21, 2026). The log is not available
at a public URL, so the normalized log lines are included below:

```text
Jul 16 10:34:11 apify-api SOFT_FAIL {"errors":[{"errorCode":"required.openapi.validation","message":"must have required property 'requestUrl'","path":"/response/data/requestUrl"}],"method":"GET","msg":"Response OpenAPI validation error","statusCode":200.0,"url":"/v2/webhooks/{webhookId}"}
Jul 20 15:56:30 apify-api SOFT_FAIL {"errors":[{"message":"no schema defined for status code '402' in the openapi spec","path":"/v2/actor-tasks/{actorTaskId}/run-sync?timeout=300&memory=1024&maxItems=20&build=latest&outputRecordKey=OUTPUT&webhooks="}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":402.0,"url":"/v2/actor-tasks/{actorTaskId}/run-sync?timeout=300&memory=1024&maxItems=20&build=latest&outputRecordKey=OUTPUT&webhooks="}
Jul 21 03:20:22 apify-api SOFT_FAIL {"errors":[{"errorCode":"type.openapi.validation","message":"must be string","path":"/response/data/webhook/requestUrl"},{"errorCode":"type.openapi.validation","message":"must be null","path":"/response/data/webhook"},{"errorCode":"anyOf.openapi.validation","message":"must match a schema in anyOf","path":"/response/data/webhook"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":201.0,"url":"/v2/webhooks/{webhookId}/test?token=[REDACTED]"}
Jul 21 18:01:10 apify-api SOFT_FAIL {"errors":[{"message":"no schema defined for status code '408' in the openapi spec","path":"/v2/actor-tasks/{actorTaskId}/run-sync?token=[REDACTED]&outputRecordKey=OUTPUT"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":408.0,"url":"/v2/actor-tasks/{actorTaskId}/run-sync?token=[REDACTED]&outputRecordKey=OUTPUT"}
```

**apify-core version:**
apify/apify-core@446d6fa

**Stop reason:** All 4 unique errors from the provided log that are
still present on `master` are fixed. The remaining 4 unique errors in
the log were already fixed on `master` by #2785 (the log lines predate
that merge, see Out of scope errors). The original errors cannot be
reproduced in the PR test environment (they come from production
traffic: non-HTTP webhooks, platform usage limits, sync-run timeouts),
so they were verified against apify-core source instead. A regression
run of the integration tests with the fixed spec completed with status
SUCCESS ([validator run
results](https://apify-pr-test-env-logs.s3.us-east-1.amazonaws.com/apify/apify-core/27741/results-44f0a8f7474fa5f462e4e79633eec56943d63a3a.html))
and its api.log contains zero response validation errors; the remaining
request validation errors in that log are deliberate malformed-request
tests or pre-existing request-side spec gaps unrelated to this diff.

## Detailed changes description
### Error fixes

#### Webhook object no longer requires `requestUrl`
- **Files:**
`apify-api/openapi/components/schemas/webhooks/Webhook.yaml:2`
- **Error:** `must have required property 'requestUrl'` at
`/response/data/requestUrl`, `GET /v2/webhooks/{webhookId}`, status 200
- **Root cause:** `requestUrl` is optional in apify-core and enforced
only for webhooks with actionType `HTTP_REQUEST`. Webhooks with other
action types (Slack message, Google Mail, Google Drive, GitHub issue)
are stored without `requestUrl`, and the API response projection (lodash
`pick`) omits missing keys, so the endpoint legitimately returns a
webhook object without `requestUrl`. A clarifying description was added
to the property.
- **Reference:**
https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/packages/schemas/src/webhooks.ts#L315

#### Webhook summary in webhook dispatch allows null `requestUrl`
- **Files:**
`apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml:10`
- **Error:** `must be string` at `/response/data/webhook/requestUrl`,
`POST /v2/webhooks/{webhookId}/test`, status 201
- **Root cause:** The test-webhook endpoint embeds the stored webhook
into the created dispatch and the response projection includes
`webhook.requestUrl`. Webhook create/update accepts `requestUrl` as
nullish (`z.string().nullish()`), so non-HTTP webhooks can be stored
with an explicit `null` value, which is then returned inside the
dispatch's webhook summary. The type was widened to `[string, "null"]`
and a clarifying description was added to the property.
- **Reference:**
https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/packages/zod-types/src/common/webhooks.ts#L57

#### Add 402 response to `POST /v2/actor-tasks/{actorTaskId}/run-sync`
- **Files:**
`apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml:144`
- **Error:** `no schema defined for status code '402' in the openapi
spec`, `POST /v2/actor-tasks/{actorTaskId}/run-sync`, status 402 (3
occurrences in the log)
- **Root cause:** Launching a task run enforces platform usage limits
which throw 402 Payment Required errors (Actor memory limit exceeded,
concurrent runs limit exceeded, not enough usage to run paid Actor).
Sibling run-sync endpoints already document 402 via the shared
`PaymentRequired` response component.
- **Reference:**
https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/packages/errors/src/errors/actor.ts#L5

#### Add 408 response to `POST /v2/actor-tasks/{actorTaskId}/run-sync`
- **Files:**
`apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml:152`
- **Error:** `no schema defined for status code '408' in the openapi
spec`, `POST /v2/actor-tasks/{actorTaskId}/run-sync`, status 408
- **Root cause:** The run-sync handler throws `run-timeout-exceeded`
with status 408 when the run does not reach a terminal status within the
maximum synchronous wait time. The GET operation of the same path and
all sibling run-sync endpoints already document 408 via the shared
`Timeout` response component.
- **Reference:**
https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/api/src/routes/actor_tasks/run_sync.ts#L68

### Refactoring
None. Only shared response components (`PaymentRequired.yaml`,
`Timeout.yaml`) are re-used; no structural changes.

## Unfixed errors
### Out of scope errors
#### `POST /v2/actors/{actorId}/builds` — no schema for status 402
- **Error:** `no schema defined for status code '402' in the openapi
spec`, status 402 (Jul 18 00:34:05)
- **Root cause:** Already fixed on `master` by #2785 (merged Jul 20);
the log line predates the merge.

#### `POST /v2/actor-tasks` — no schema for status 404
- **Error:** `no schema defined for status code '404' in the openapi
spec`, status 404 (Jul 18 02:50:38)
- **Root cause:** Already fixed on `master` by #2785; the log line
predates the merge.

#### `GET /v2/actors/{actorId}` — `taggedBuilds` `buildNumber` type
error
- **Error:** `must be string` at
`/response/data/taggedBuilds/{tag}/buildNumber`, status 200 (Jul 20
08:15:38)
- **Root cause:** Already fixed on `master` by #2785 (`buildNumber` made
nullable); the log line predates the merge.

#### `PUT /v2/actor-tasks/{actorTaskId}` — no schema for status 409
- **Error:** `no schema defined for status code '409' in the openapi
spec`, status 409 (Jul 20 08:47:37)
- **Root cause:** Already fixed on `master` by #2785; the log line
predates the merge.

## Issues
Partially implements: #2286

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adhoc Ad-hoc unplanned task added during the sprint. 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.

4 participants