docs(openapi): Autofix OpenAPI spec validation errors - #2785
Conversation
…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
|
🗑️ Preview for this PR was deleted. |
|
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 A companion PR has been opened in
|
…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
|
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 The companion
|
…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).
## 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>
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.logexcerpt (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: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
apify-api/openapi/paths/actors/acts@{actorId}@builds.yaml:123{"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}POST /v2/actors/{actorId}/buildsenforces the account memory and concurrent-runs limits before creating the build and throwsmemory-limit-exceeded/concurrent-runs-limit-exceedederrors with HTTP 402, but the spec did not document a 402 response. Reused thePaymentRequiredresponse component, consistent with the run Actor endpoints that document 402 for the same limit checks.Add 404 (Not Found) response to Create task endpoint
apify-api/openapi/paths/actor-tasks/actor-tasks.yaml:142{"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}POST /v2/actor-taskslooks up the Actor referenced byactIdin the request payload and throwsrecord-not-foundwith HTTP 404 when that Actor does not exist or is removed, but the spec did not document a 404 response. Reused theNotFoundresponse component.Allow null buildNumber in tagged builds of the Actor object
apify-api/openapi/components/schemas/actors/TaggedBuildInfo.yaml:10{"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}"}GET /v2/actors/{actorId}builds thetaggedBuildsmap viatransformActor(), which computesbuildNumberasbuildOrVersionNumberIntToStr(buildNumberInt)with return typestring | null; for legacy builds without a validbuildNumberIntthe value isnull, while the spec allowed only a string. AllowednullforbuildNumber(thepatternkeyword only applies to string values, so it keeps validating real build numbers).Add 409 (Conflict) response to Update task endpoint
apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}.yaml:104{"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}PUT /v2/actor-tasks/{actorTaskId}throwsactor-task-name-not-uniquewith HTTP 409 when renaming a task to a name already used by another task of the same user (Mongo duplicate key onuserId+nameLowerCase), but the spec did not document a 409 response. Reused theConflictresponse component, matchingPOST /v2/actor-taskswhich already documents 409 for the same uniqueness constraint.Add cannot-monetize-without-payout-billing-info to ErrorType enum
apify-api/openapi/components/schemas/common/ErrorType.yaml:68{"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)PUT /v2/actors/{actorId}with a paid pricing model rejects the update with HTTP 400 and error typecannot-monetize-without-payout-billing-infowhen the Actor owner has no payout billing info set (thrown bycheckAndSanitizePricingInfosModifier), but this error type was missing from theErrorTypeenum shared by all error responses.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
{"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)standbyDeploymentquery parameter that is not yet documented onPOST /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
{"url":"/v2/actors/{actorId}/oauth-connections","method":"GET","errors":[{"message":"not found","path":"/actors/{actorId}/oauth-connections"}]}(request validation, regression run log)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
input/options/generalAccess/eventTypes/filter/handledAtvalues, 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=...).Issues
Partially implements: #2286