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>
* feat: Add request body compression with optional brotli (apify#927) ## Description - https://github.com/apify/apify-core/pull/28971 added support for brotli compression to BE. - Pros: higher compression, cons: more CPU intensive. - The brotli dependencies are defined as optional, one has to explicitly enable it and choose one. - JS client doesn't compress requests that are too small, this Python client compresses just everything. No change done, I only noticed and stating it. ## Issues Closes: apify#942 ## Related PRs - https://github.com/apify/apify-core/pull/28971 - apify/apify-docs#2750 - apify/apify-client-js#962 - apify#927 - apify/apify-sdk-python#1031 --------- Co-authored-by: Vlada Dusek <v.dusek96@gmail.com> * chore(release): Update changelog and package version [skip ci] * chore: Automatic docs theme update [skip ci] * test: Deflake test_schedule_list and test_task_list by polling eventually consistent listings (apify#951) `test_schedule_list` and `test_task_list` failed in CI ([schedule run](https://github.com/apify/apify-client-python/actions/runs/29497290807/job/87617167353), [task run](https://github.com/apify/apify-client-python/actions/runs/29498108478/job/87619855853)) because they assert read-your-write on listing endpoints: they list resources immediately after creating them, and under load the listing can serve a view that hasn't yet caught up with the creates, so the fresh IDs are sometimes missing. The creates themselves succeeded in both cases, so these are eventual-consistency flakes, not client bugs. The fix wraps each list read in the existing `poll_until_condition` helper (30 s ceiling), waiting until the created IDs appear in the listing. The original assertions still run on the final page, so a real regression still fails. Follows the same deflaking pattern as apify#824, apify#831, apify#844, and apify#868. * docs: fix input guide example to bound the wait with wait_duration (apify#955) The "Pass input to an Actor" guide example claimed that `timeout=timedelta(seconds=60)` makes `call()` wait up to 60 seconds for the run to finish. It doesn't. On `call()`, `timeout` is the per-request HTTP timeout forwarded to `start()`, while the wait cap is `wait_duration` (default `None`, i.e. wait indefinitely). A user copying the example would block indefinitely on a long or never-finishing run. The sync and async examples now use `wait_duration=timedelta(seconds=60)` and the comment is reworded accordingly. The stale copies under `website/versioned_docs/` are left untouched on purpose. The versioning workflow deletes and re-snapshots `version-3.0` from `docs/` on every 3.x release, so the fix propagates there automatically. * chore: Automatic docs theme update [skip ci] * fix: offload async request body compression to a worker thread (apify#950) The async `ImpitHttpClientAsync.call()` serialized and compressed request bodies inline on the event loop. Compression is CPU-bound (and brotli is becoming the default via the Apify SDK), so it blocked the loop for the whole duration of the compression, stalling every other concurrent task (response reads, the platform events websocket, other in-flight requests). Request preparation is now offloaded to a worker thread via `asyncio.to_thread` whenever there is a body to compress. Bodyless requests (the common polling, listing, and get path) stay inline to avoid a needless thread-dispatch hop. The synchronous client is unchanged, as it has no event loop to block. * chore(release): Update changelog and package version [skip ci] * fix: Propagate last_run status/origin filters to chained storage clients (apify#954) The `status` and `origin` filters passed to `ActorClient.last_run(...)` and `TaskClient.last_run(...)` were silently dropped by the chained storage clients. For example, `actor.last_run(status='SUCCEEDED').dataset().list_items()` queried the most recent run's dataset regardless of status. This is a regression vs 1.x, where `_sub_resource_init_options` forwarded the parent's params to every child client; the typed-client refactor (apify#604) lost that. The fix passes the run client's default params to its four child-client factories (`dataset()`, `key_value_store()`, `request_queue()`, `log()`) in both `RunClient` and `RunClientAsync`, so the filters ride along on the `runs/last` storage endpoints again, matching 1.x and the JS client. Adds a parametrized regression test covering all four chained clients, sync and async. All 8 cases fail before the fix and pass after. * chore(release): Update changelog and package version [skip ci] * chore: Automatic docs theme update [skip ci] * fix: Propagate API token to custom HTTP clients (apify#956) `ApifyClient.with_custom_http_client(token=...)` (and the async twin) stored the token on the `ApifyClient` instance but never passed it to the injected HTTP client, so no request carried an `Authorization` header and every call failed with 401. The documented custom HTTP client example inherited the bug. - `with_custom_http_client` now sets `Authorization: Bearer <token>` on the injected client's default headers, unless the client already has an auth header configured (checked case-insensitively). - `HttpClientBase._prepare_request_call` now merges the client's default headers under the per-request headers, so any custom client using the helper (including a pre-built `ImpitHttpClient` passed as the custom client) actually sends them. For the default client the wire behavior is unchanged, since impit request-level headers replace the identical client-level ones. - The HTTPX guide examples now merge `self._headers` before delegating, and the `HttpClient` ABC docstring states that implementations must send the default headers with every request. Regression tests cover the token reaching the wire (custom sync/async clients and a pre-built tokenless `ImpitHttpClient`) and the no-clobber semantics for client-configured auth headers. * chore(release): Update changelog and package version [skip ci] * fix: Make batch_add_requests split batches by serialized payload size (apify#953) The 9 MB payload guard in `batch_add_requests` was inert: `constrained_batches` was called without `get_len`, so the default `len()` measured each request dict's key count (~4) instead of its serialized size. Batches were therefore split only by the 25-request count limit, and large requests shipped as one oversized POST that the API rejects with 413, failing the whole call. The guard now measures each request as its UTF-8 JSON byte length, using the same serialization flags as the HTTP client's request body path. It also passes `strict=False`, which preserves the previous contract for an individually oversized request: it's sent in its own batch and left for the API to judge, instead of raising a client-side `ValueError`. Both the size-based splitting and the oversized-singleton path are covered by new sync/async regression tests. *✍️ Drafted by Claude Code* * chore(release): Update changelog and package version [skip ci] * docs: Fix documentation mismatches with actual client behavior (apify#958) Fixes 12 documentation issues found by an engineering audit, where docs, examples, or docstrings contradict what the client actually does: - The custom HTTP client guide example (`HttpxClient`) now raises `ApifyApiError` for error responses instead of returning them raw, and the guide documents this part of the `call` contract (resource clients rely on it, e.g. to translate a 404 into a `None` return value). - The streaming concepts page no longer claims all three streaming methods yield a raw `impit.Response` — it now describes the actual yielded value per method (`stream_record` yields a `dict` with the response under `value`, `stream` and `stream_record` may yield `None`). - The pagination concepts page no longer lists `ListOfRequests` among page models exposing `total`/`offset`/`count`; it now explains its cursor-based pagination via `next_cursor`. - The logging formatter example no longer references `%(status_code)s` (absent on most records, causing logging errors) and no longer attaches a duplicate handler; the page notes which properties are present on every record. - The upgrading-to-v3 guide cross-links now include the site baseUrl (`/api/client/python/...`), fixing 6 links that 404ed on the published site. - The conda instruction for the brotli extra installs `brotli-python` (the Python bindings) instead of `brotli` (the C library). - `RunClient.resurrect` docstrings cite the real `SUCCEEDED` status instead of the nonexistent `FINISHED`. - `wait_for_finish` docstrings in `RunClient` and `BuildClient` spell the terminal status as `TIMED-OUT` (the real literal) instead of `TIMED_OUT`. - The quick-start page refers to the `Run` model's `default_dataset_id` attribute instead of the v2-era run dictionary with `defaultDatasetId`. - The README dataset example passes `fields` as `list[str]` per the signature instead of a comma-separated string. - The README quick-start examples handle the `Run | None` return of `call()` instead of accessing attributes on a possible `None`. - The timeouts concepts page states that `no_timeout` is capped at 24 hours by the default client instead of claiming it disables the timeout entirely. *✍️ Drafted by Claude Code* * chore(release): Update changelog and package version [skip ci] * docs: Version docs for v3.1.0 [skip ci] * chore: Automatic docs theme update [skip ci] * chore: Automatic docs theme update [skip ci] * fix: Add missing cannot-monetize-without-payout-billing-info error code (apify#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). * chore(release): Update changelog and package version [skip ci] * chore(deps): update pnpm to v11.15.1 (apify#967) * chore(deps): update dependency oxfmt to ^0.59.0 (apify#966) * chore(deps): lock file maintenance (apify#968) * fix: Normalize query params in dataset create_items_public_url (apify#963) `DatasetClient.create_items_public_url` (and its async twin) passed `_build_params` output straight to `urlencode`, bypassing the `_parse_params` normalization that the real HTTP request path applies. As a result, boolean and list query params ended up as Python reprs in the signed URL — e.g. `create_items_public_url(clean=True, fields=['title', 'url'])` produced `clean=True&fields=%5B%27title%27%2C+%27url%27%5D` instead of `clean=true&fields=title,url`. Consumers of the shared URL then got unclean/unfiltered items or an API error. The fix routes the params through `self._http_client._parse_params(...)` before `urlencode`, so the public URL matches exactly what an actual API request would send (bool→`true`/`false`, list→comma-joined, `None` dropped). Added regression tests for both the sync and async clients. *✍️ Drafted by Claude Code* * chore(release): Update changelog and package version [skip ci] * chore(deps): update actions/setup-node action to v7 (apify#969) * chore(deps): update actions/setup-python action to v7 (apify#970) * docs: Remove conda special instructions for brotli (apify#959) It is now included by default: https://github.com/conda-forge/apify-client-feedstock/pull/4/changes * docs: Update description of `request_url` in `Webhooks` (apify#974) - Updates the auto-generated Pydantic models and TypedDicts based on the proposed OpenAPI specification changes. - Based on apify-docs PR [#2794](apify/apify-docs#2794). --------- Co-authored-by: Michal Turek <michal.turek@apify.com> Co-authored-by: Vlada Dusek <v.dusek96@gmail.com> Co-authored-by: Apify Service Account <64261774+apify-service-account@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Josef Procházka <josef.prochazka@apify.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