Repository navigation
fix: Report an empty search-actors page past the last result as such - #1568
Merged
Merged
Conversation
search-actors and search-actors-widget returned the no-match keyword guidance for any empty page, including a page past the last result of a query that matched. With offset > 0 the text is now "No Actors past offset <offset> for the search query "<keywords>"." The offset 0 text and the response fields are unchanged. Closes #1276 Claude-Session: https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC
This was referenced Oct 14, 2026
jirispilka
added a commit
that referenced
this pull request
Oct 14, 2026
…#1565) ## Why Closes #1561 Review page with before/after examples for this PR and the five related ones (#1563, #1564, #1566–#1568): https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i Tool errors with an HTTP status that has no specific hint (400, 404, 405, 409, …) ended with "Verify the tool name and input parameters.", though the tool name was right and the input valid. A missing run, for example, read as if the call itself was wrong. ## What changed The generic hint is now appended only when the error has no HTTP status. With a status, the 401, 403, 429 and 5xx hints stay; any other status adds nothing, since the text already carries the API's message and error type. The run-limit and cannot-publish branches are unchanged. `abort-actor-run` with a nonexistent `runId` (404): ``` Before: Error calling tool "abort-actor-run": Actor run was not found (API error type: record-not-found). Verify the tool name and input parameters. Now: Error calling tool "abort-actor-run": Actor run was not found (API error type: record-not-found). ``` An error without a status (a network failure, a plain `Error`) still ends with "Verify the tool name and input parameters.". This is a tool-text change to the error text every tool returns; no eval case references the removed hint. ## Notes for reviewers (human-written) ## Proof it works mcpc with the Claude connector tool set, this branch against a build with master's `src/utils/mcp.ts`: | Call | master | this branch | |---|---|---| | `abort-actor-run runId:="doesNotExist123"` | `… Actor run was not found (API error type: record-not-found). Verify the tool name and input parameters.` | `… Actor run was not found (API error type: record-not-found).` | | `get-actor-run runId:="doesNotExist123"` | `Run with ID 'doesNotExist123' not found.` | unchanged (the tool words its own not-found error) | | `get-key-value-store-record keyValueStoreId:="doesNotExist123" recordKey:="INPUT"` | `Key-value store 'doesNotExist123' not found.` | unchanged (own error) | Unit tests (the 400 and 404 cases failing before the fix): `getToolCallErrorUserText()` for 400 and 404 (no hint), 401, 403, 429 and 500 (hint kept) and an error without a status (generic hint kept), and a `tools/call` of a tool that throws a 404. Implemented with Claude Code: code, tests, mcpc probe and this description. Reviewed by a separate Claude Code review pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC --- _Generated by [Claude Code](https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC)_ Co-authored-by: Claude <noreply@anthropic.com>
jirispilka
added a commit
that referenced
this pull request
Oct 14, 2026
) ## Why Closes apify/ai-team#334 Review page with before/after examples for this PR and the five related ones (#1564–#1568): https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i `resources/read` and `apify-api-read` returned the account's proxy password from `/v2/users/me` in plain text. `resources/read` is served in every session. ## What changed The JSON redaction that both paths share now covers the `password` key as well as `urlSigningSecretKey`: the value becomes `[REDACTED]`, the key stays. The function is renamed from `redactUrlSigningSecretKey` to `redactJsonSecrets`. `apify-api-read` with `path: /v2/users/me`, or `resources/read` on the same URL: ``` Before: "proxy": { "password": "<the proxy password>", "groups": [...] } Now: "proxy": { "password": "[REDACTED]", "groups": [...] } ``` Any `password` string value in a JSON body is redacted, for example in dataset items or a JSON key-value store record, and also in `apify-api-write` responses, which go through the same function. The key match is exact (`"password"`, case-sensitive). Text, XML and binary bodies are unchanged, as for `urlSigningSecretKey`. The response shape, including `structuredContent`, stays the same; only those values change. The function is not exported through `internals.js`. ## Notes for reviewers (human-written) ## Proof it works mcpc on a local build (`--tools=apify-api-read`), checking the field with jq without printing the body: | Check | Result | |---|---| | `resources-read https://api.apify.com/v2/users/me`: `.data.proxy.password == "[REDACTED]"` | `true` | | same: `.data.proxy` still has `password`, `groups` is an array | `true`, `"array"` | | same: a proxy password appears anywhere in the text | `false` | | `tools-call apify-api-read path:="/v2/users/me"`: `structuredContent` and `content[0].text` password `== "[REDACTED]"` | `true`, `true` | | same: strings holding a proxy password anywhere in the result | `0` | | same: `isError`, `statusCode` | `false`, `200` | New unit tests (failing before the fix) cover `redactJsonSecrets()`, `readApiResource()` and `apify-api-read` on a `/v2/users/me`-shaped body with a fake value. Implemented with Claude Code: code, tests, mcpc probe and this description. Reviewed by a separate Claude Code review pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC --- _Generated by [Claude Code](https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC)_ Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jirispilka
marked this pull request as ready for review
October 14, 2026 09:17
vojtechj-apify
approved these changes
Oct 15, 2026
jirispilka
added a commit
that referenced
this pull request
Oct 15, 2026
…ts (#1567) ## Why Closes apify/ai-team#335 Review page with before/after examples for this PR and the five related ones (#1563–#1566, #1568): https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i `get-dataset-list` and `get-key-value-store-list` default to `unnamed=false`, so they list only named storages, while Actor runs write to unnamed ones. The response did not say the list was limited, and an empty page pointed at inspecting an entry of the empty list. Nothing served said how long unnamed storages are kept. ## What changed Summary with `unnamed=false` (the default) says "named"; with `unnamed=true` it is unchanged. On an empty page with no next page, `nextStep` states what `unnamed=true` adds instead of pointing at the empty list. Pagination hints and the hint on a nonempty last page are unchanged; tool descriptions and defaults are unchanged. | `get-dataset-list` call (test account: 11 named, 855 unnamed datasets) | Before | Now | |---|---|---| | defaults | `Listed 10 of 11 datasets.` | `Listed 10 of 11 named datasets.` | | `unnamed=true` | `Listed 10 of 855 datasets.` | unchanged | | empty page, defaults | `Listed 0 of 11 datasets.` / `Use get-dataset with a datasetId from the list to inspect a dataset.` | `Listed 0 of 11 named datasets.` / `unnamed=true includes the unnamed datasets that Actor runs create.` | | empty page, `unnamed=true` | `Listed 0 of 855 datasets.` / `Use get-dataset with a datasetId from the list to inspect a dataset.` | `Listed 0 of 855 datasets.` / `No follow-up step.` | `get-key-value-store-list` does the same with "key-value stores". The server instructions' `## Storage types` section gets one bullet: ``` - Unnamed storages are deleted after the account's data-retention period; named storages are kept until deleted. ``` The new text names no tool and asks for no call. This is a tool-text change to `structuredContent.summary`/`nextStep` of both list tools and to the server instructions. ## Notes for reviewers (human-written) ## Proof it works mcpc with the Claude connector tool set; counts checked with `apify datasets ls` / `apify key-value-stores ls` (with and without `--unnamed`). The table above is this probe: "Before" on a build with master's storage code, "Now" on this branch. Empty pages use an offset past the end, so no storage was created or deleted. `get-key-value-store-list` on the same account (18 named, 789 unnamed stores): | Call | `summary` | `nextStep` | |---|---|---| | defaults | `Listed 10 of 18 named key-value stores.` | `Call get-key-value-store-list again with offset=10 to fetch the next page.` | | `unnamed=true` | `Listed 10 of 789 key-value stores.` | `Call get-key-value-store-list again with offset=10 to fetch the next page.` | | `offset=100000` | `Listed 0 of 18 named key-value stores.` | `unnamed=true includes the unnamed key-value stores that Actor runs create.` | | `offset=100000`, `unnamed=true` | `Listed 0 of 789 key-value stores.` | `No follow-up step.` | `content[1].text` is `summary` + newline + `nextStep`, and the retention bullet is in the served instructions. Unit tests: the `named` summary on default pages, both empty-page `nextStep` variants (including an offset past the end) with schema validation against `storageListOutputSchema`, and the retention bullet in `## Storage types` fail before the fix. Guards keep the `unnamed=true` summary and the inspect hint on a nonempty last page unchanged. Implemented with Claude Code: code, tests, mcpc probe and this description. Reviewed by a separate Claude Code review pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC --- _Generated by [Claude Code](https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
jirispilka
added a commit
that referenced
this pull request
Oct 15, 2026
## Why Closes #1560 Review page with before/after examples for this PR and the five related ones (#1563–#1565, #1567, #1568): https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i Aborting a run that had already finished returns it unchanged, and `abort-actor-run` reported it like any status read ("SUCCEEDED in 68s. …"), with no word that the abort did nothing. An already-ABORTED run looked the same as an abort that took effect. ## What changed `abort-actor-run` reads the run before aborting. When the status is SUCCEEDED, FAILED, TIMED-OUT or ABORTED, it sends no abort and `summary` starts with "Nothing was aborted: the run had already finished." Any other status is aborted as before, and the returned ABORTING or ABORTED text is unchanged. `nextStep` and the output schema are unchanged. This is a tool-text change to `structuredContent.summary` and the narrative text. `abort-actor-run` on an ABORTED run: ``` Before: ABORTED after 9.612s. Actor status: "Finished! Total 1 requests: 1 succeeded, 0 failed". Now: Nothing was aborted: the run had already finished. ABORTED after 9.612s. Actor status: "Finished! Total 1 requests: 1 succeeded, 0 failed". ``` An unknown `runId` now returns the text `get-actor-run` uses, from the read instead of the abort: ``` Before: Error calling tool "abort-actor-run": Actor run was not found (API error type: record-not-found). Now: Run with ID 'doesNotExist123' not found. ``` Aborting a running run now takes two API calls (read, then abort); a finished run takes one, as before. If a run finishes between the read and the abort, the response shows its final status without the prefix. `structuredContent` keeps its shape; only the `summary` of a finished run and the not-found text change, which matters if the hosted server's contract tests pin either. ## Differs from #1560 The issue proposed checking the status the abort call returns, prefixing only SUCCEEDED, FAILED and TIMED-OUT, and keeping today's text for ABORTING and ABORTED. This PR does three things differently: - **Reads the run before the abort.** The abort response for a run that was already ABORTED looks the same as one for a run this call just aborted, so only a read beforehand can tell them apart. The cost is one extra API call per abort of an unfinished run. - **Prefixes already-ABORTED runs too.** Nothing was aborted for them either. - **Changes the not-found text.** It now comes from the read, so it matches `get-actor-run`. ## Notes for reviewers (human-written) ## Proof it works mcpc with the Claude connector tool set, `abort-actor-run` on finished runs from the account (`get-actor-run` before and after shows the same status and `finishedAt`; the calls changed nothing): | Run | Status | `structuredContent.summary` | |---|---|---| | `Nz8MuAIqzMUiusK4A` | SUCCEEDED | Nothing was aborted: the run had already finished. SUCCEEDED in 68.317s. Actor status: "Finished! Scraped 2 pages, 0 failed". | | `SCbQwsYRhX8vmarD3` | FAILED | Nothing was aborted: the run had already finished. FAILED after 96.95s. | | `O3KXkXfnQfLp8X13n` | ABORTED | Nothing was aborted: the run had already finished. ABORTED after 9.612s. Actor status: "Finished! Total 1 requests: 1 succeeded, 0 failed". | | `b08qGCehNYxW69XI4` | ABORTED | Nothing was aborted: the run had already finished. ABORTED after 12.799s. Actor status: "Starting the crawler". | | `doesNotExist123` | – | `isError: true`, `Run with ID 'doesNotExist123' not found.` | `content[1].text` starts with the same summary, and `content[0]` equals `structuredContent`. The probe started no run, so aborting a running run is covered by unit tests only. Unit tests (failing before the fix): for SUCCEEDED, FAILED, TIMED-OUT and ABORTED runs no abort is sent and the summary starts with the sentence, `content[0]` mirrors `structuredContent`, and the response validates against `actorRunOutputSchema`; an unknown run returns the not-found error without an abort. READY, RUNNING, TIMING-OUT and ABORTING runs are aborted once, and an ABORTING or ABORTED response keeps its summary. Implemented with Claude Code: code, tests, mcpc probe and this description. Reviewed by a separate Claude Code review pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC --- _Generated by [Claude Code](https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 15, 2026
jirispilka
added a commit
that referenced
this pull request
Oct 16, 2026
…1600) ## Why Closes #1593 The Store search API pages before it filters, so a page at a later offset can be empty while later offsets still return Actors. Since #1568, `search-actors` and `search-actors-widget` described such a page as `No Actors past offset N…`, which says the results ended. ## What changed Empty page at `offset > 0`: ``` Before: No Actors past offset 8 for the search query "czech real estate". Now: No Actors returned at offset 8 for the search query "czech real estate". ``` The `offset: 0` keyword guidance is unchanged. The search helper still returns items only; no extra API call, paging logic or retry text. The text is a tool-text contract change: it is `structuredContent.instructions` and the text content of `search-actors`, and the text content of `search-actors-widget`. The widget's `structuredContent` keys are unchanged. ## Notes for reviewers (human-written) ## Proof it works Tests first: the default and widget response tests failed against the old wording, then passed after the fix. They cover offset-zero guidance and empty later pages (offset 8 for the default response, offset 1 for the widget). Duplicate helper-only tests were removed; all cases are mocked. mcpc against a local build of this branch (`PORT=3003 pnpm start`), the Claude connector selection (`?tools=…&client=claude`, 21 tools; with `&ui=true`, 23 tools), protocol `2026-07-28`. Live Store, `limit: 1`: | Call | Result | |---|---| | `search-actors` `linkedin jobs`, offset 3 | `cheap_scraper/linkedin-job-scraper` | | `search-actors` `linkedin jobs`, offset 4 | `No Actors returned at offset 4 for the search query "linkedin jobs".` | | `search-actors` `linkedin jobs`, offset 5 | `valig/linkedin-jobs-scraper` | | `search-actors` `czech real estate`, offset 7 | `parseforge/sreality-cz-scraper` | | `search-actors` `czech real estate`, offset 8 | `No Actors returned at offset 8 for the search query "czech real estate".` | | `search-actors` `czech real estate`, offset 9 | `solidcode/reality-idnes-cz-scraper` | | `search-actors-widget` (Apps mode) `linkedin jobs`, offset 4 | same sentence; `structuredContent` keys `actors`, `count`, `query`, `widgetActors` (`[]`) | | `search-actors-widget` (Apps mode) `czech real estate`, offset 8 | same sentence, same keys | | `search-actors` (Apps mode) `czech real estate`, offset 8 | same sentence in text and `structuredContent.instructions` | | `search-actors` `zzqqxxnotarealactorkeyword123`, offset 0 | the three-line keyword guidance, unchanged | The same offsets on protocol `2025-11-25`, default and Apps mode, give the same results. On a build of current master (same selection), `linkedin jobs` offset 4 and `czech real estate` offset 8 return `No Actors past offset …`. Implemented with Claude Code (code, tests, mcpc probes, this description). A separate Claude Code review pass found no issues. Test/comment cleanup assisted by Codex; all five required checks pass after the trim. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011QiQRhqm3wKyv6KFLncAqG --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Closes #1276
Review page with before/after examples for this PR and the five related ones (#1563–#1567): https://claude.ai/artifact/WDRj1nwPnMTP9ktGyg3z3i
Reported by @lokicik. This follows the implementation spec in the maintainer comment on the issue.
search-actorsreturned the no-match guidance ("Keywords can include … A platform-only query such as "TikTok" …") for any empty page, including a page past the last result of a query that matched. With an empty query that advice cannot help, and it reads as if nothing matched.What changed
buildNoActorsFoundInstructions(), shared bysearch-actorsandsearch-actors-widget, now gets the offset. Withoffset > 0it returns one sentence and no keyword advice; atoffset0 the text is unchanged. No new response fields; the output schema is unchanged. This is a tool-text change tostructuredContent.instructionsand the text content.search-actorswithkeywords: "", limit: 1, offset: 1000000:Notes for reviewers (human-written)
Proof it works
mcpc with the Claude connector tool set on this branch:
search-actorscallkeywords: "", limit: 1, offset: 0keywords: "", limit: 1, offset: 1000000No Actors past offset 1000000 for the search query "".keywords: "instagram scraper", limit: 5, offset: 5000No Actors past offset 5000 for the search query "instagram scraper".keywords: "zzqqxxnotarealactorkeyword123", offset: 0The text content equals
structuredContent.instructions, andstructuredContentkeeps its fields (actors: [],query,count: 0,instructions). The sameoffset: 1000000call on a build with master's text returns the three-line guidance shown under "Before".Unit tests (failing before the fix): an empty page at
offset1 returns the new sentence instructuredContentand the text content, for bothsearch-actorsandsearch-actors-widget. The existing empty-first-page tests keep the three-line text.Implemented with Claude Code: code, tests, mcpc probe and this description. Reviewed by a separate Claude Code review pass.
🤖 Generated with Claude Code
https://claude.ai/code/session_0149C4bPjve1UHWcxTtBsPXC
Generated by Claude Code