Repository navigation
feat(batch): agents could not recover, inspect or manage batch jobs from the CLI - #63
Open
emirlan-zen wants to merge 4 commits into
Open
emirlan-zen wants to merge 4 commits into
emirlan-zen wants to merge 4 commits into
Conversation
…rom the CLI batch cancel misread the stopped run and always reported status unknown, runs that ran out of credits got advice to fix their tasks, retry-failed could not reach the tasks a stop left pending, the default policy blocked any Adaptive Stealth batch over 400 URLs, and the estimate ignored the job-level flags it would submit. Fix those, and follow the API and the crawl commands for errors, waits and retries. - cancel, pause and resume read the run the API answers with; cancel on an ended run prints the job as it ended - a failed run gets the code and advice of the matching refused request (out of credits, inactive subscription, key cap) with server_code and job_id, and the command that reruns its unfinished tasks - rerun with --status pending,processing,failed,successful or a full rerun; retry-failed --include-pending - each lifecycle 409, the 410 and the content 422 get their own code and advice; every error carries server_code and job_id - create and rerun send an Idempotency-Key and retry transient failures under it; reads retry 429/5xx; an unknown outcome prints the safe repeat - POST tasks send method and body at task level; metadata, external_id, url and body are checked against the API's limits before submitting - the estimate prices job-level flags, nested zenrows_params and "false", as a range for Adaptive Stealth Mode; estimate takes create's flags - default policy caps raised to 10,000 URLs and 250,000 estimated credits - --timeout takes 30s, 5m, 1h (a bare number stays milliseconds) and is validated; create --follow takes it; a wait that runs out or meets a paused run exits 0 with a note; Ctrl-C stops the wait and exits 130 - JSON output keeps job_id, run_id, paused, ingest_status, accepted_tasks and rerun counts, and the full job, alongside the existing keys - new: batch list, content, pause, resume, rerun, delete; create takes --name, --external-id and --metadata
zenrows batch rerun with no flag now reruns the failed and pending tasks, matching the MCP batch_rerun default. --status picks the statuses and --all reruns every task; the two cannot be combined.
… endless wait - once an attempt of a keyed create or rerun got no answer, any later refusal (a 402 included, which the API checks before the key) keeps the Idempotency-Key and the safe-repeat advice - the repeat command shell-quotes every argument and the key and keeps their whitespace - a wait passes its deadline to each request: attempts are cut off at it and no retry sleeps past it, so --timeout holds - results whose run state cannot be read are reported as possibly partial - url and metadata value limits count UTF-8 bytes, and the job name counts code points, as the API does
emirlan-zen
marked this pull request as ready for review
October 16, 2026 14:29
This branch has not been deployed
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
zenrows batchdisagreed with the Batch API in ways that sent agents down the wrong path:batch cancelread the stopped run as a job, so it always printedstatus: unknown.BATCH_FAILEDand "fix the tasks, then retry-failed". There was nothing to fix in the tasks, andretry-failedonly reranfailedtasks. The tasks the failure leftpendingcould not be reached at all.--premium-proxypriced at 1 instead of 10) and read"false"as on.methodandbodyinsidezenrows_params, so the API rejected the whole job.BATCH_FAILED"Fix the reported problem", with noserver_code.createsent noIdempotency-Key. A timed-out submit that the API had accepted could be resubmitted as a second, billed job.waithung on a paused run, ignored--timeout 10s, and exited 1 with no state when the time ran out.What changes
Fixes
cancel,pauseandresumeread the run the API answers with.cancelon an ended run prints "Nothing to stop" and the job as it ended, the waycrawl stopdoes.A failed run gets the code and advice of the matching refused request:
insufficient_creditsmaps toPOLICY_MAX_CREDITS_EXCEEDED.subscription_inactivemaps toAUTH_INVALIDwith Billing advice.api_key_cap_reachedmaps toKEY_CREDIT_CAP_REACHED.BATCH_FAILED.Each carries
server_code(thefailure_reason),job_id, and the command that reruns the unfinished tasks. The exit code is 1.Each lifecycle 409 gets its own code and next action:
BATCH_RUN_IN_PROGRESS,BATCH_RUN_ENDED,BATCH_NO_MATCHING_TASKS,BATCH_RERUN_WINDOW_EXPIRED,BATCH_REQUEST_IN_FLIGHT, andBATCH_CONFLICTfor the rest. The 410 isBATCH_RESULT_EXPIRED, a content 422 isBATCH_TASK_FAILED, and a content 409 isBATCH_TASK_NOT_READY.Other error fixes:
server_codeandjob_id.zenrows batch list/zenrows crawl listinstead of a<id>placeholder.createandrerunsend anIdempotency-Key, generated or from--idempotency-key. They retry network errors and 502/503/504 up to 3 times under the same key.cancel,pauseandresumeretry 502/503/504.idempotency_keyand the exact, shell-quoted command that repeats the request safely. This also holds when a later attempt is refused (for example a 402, which the API checks before the key) after an earlier attempt got no answer.POST tasks send
methodandbodyat task level. They get no Adaptive Stealth Mode, and they are refused locally withjs_render,js_instructionsorjson_response.estimateandcreatecheck each line against the API's limits before anything is sent:url: http or https, 2048 bytes at most.external_id: 128 characters at most, from[A-Za-z0-9._-].metadata: 20 string entries, keys up to 64 characters, values up to 1024 bytes, 4 KiB in total.body: 16 KiB at most.The estimate prices tasks on what is submitted: job-level flags, nested
zenrows_params, and"false". Adaptive Stealth Mode is priced as a range (1 to 25).estimatetakescreate's flags. The JSON addsestimated_creditsandestimated_credits_min.Default policy caps: 10,000 URLs and 250,000 estimated credits per run, up from 1,000 and 10,000. With these defaults, a run of up to 10,000 URLs passes in any mode, since an Adaptive Stealth Mode task counts at its 25-credit upper bound.
POLICY_LIMIT_EXCEEDEDnow prints the exactzenrows policy set ...command. Workspaces created earlier keep the caps stored in theirpolicy.json.--timeoutchanges:500ms,30s,5mor1h. A bare number keeps its old meaning, milliseconds.create --followandrerun --followtake it.Wait changes:
note, the waycrawl waitdoes.paused: trueand a resume note.results:--status processingand--run <run_id>.partial: truewhile the run is in progress or still receiving its tasks, or when the run's state cannot be read.--download, skips expired rows with the reason and counts them.JSON output adds
job_id,run_id,paused,ingest_status,accepted_tasks,rerun_of,retried_tasks,inherited_tasksand the fulljob. Existing keys (jobId,estimatedCredits, ...) stay.New commands and flags
batch list [--status open|closed] [--type regular|scheduled] [--limit n] [--cursor c]batch content <id> <task_id> [--run <run_id>] [--out <file>]batch pause <id>,batch resume <id>batch rerun <id>reruns thefailedandpendingtasks, so successful tasks are never billed again by surprise. This matches the MCPbatch_rerundefault.--status pending,processing,failed,successfulpicks the statuses, and--allreruns every task.--alland--statuscannot be combined.batch retry-failed <id> --include-pending, the same asrerun --status failed,pending,processing.batch delete <id> --yescreate --name,--external-idand--metadata key=value(repeatable).Schedules, webhooks, ZIP exports, CSV inputs, open jobs, the runs list and task history stay API/SDK-only. The help text, README and skills say so.
Docs: help text, the README Batch section,
skills/batch-jobs(rewritten in the crawl skill's layout, with Limits and Errors sections),skills/trace-debug,skills/zenrows, the MCP copy inagent-plugin/skills/batch-jobs, the template and the recipe (--followinstead of--wait).Behaviour changes to call out in the release notes
batch waitandcreate --followexit 0, not 1, when the wait runs out. The output carries the state and anote.--timeoutvalues are rejected.BATCH_FAILED.POLICY_MAX_CREDITS_EXCEEDED, and a subscription-failed run isAUTH_INVALID, instead ofBATCH_FAILED.crawl --max-pagesis capped by the samemax_pages_per_run, so its local default cap also rises to 10000.Overlap with open PRs
README.md,skills/crawl/SKILL.mdand thecrawlhelp text sit inside fix(crawl): say a crawl with nothing new to follow completes, so users stop chasing their patterns #50's hunks. These are one-line conflicts.skills/zenrows/SKILL.mdline 82 ("cheapest path at scale") is next to feat(extract): remove the autoparse fallback and add zenrows extract prepare #47's hunk.src/core/errors.tsonly gets added lines (new codes,job_id,idempotency_key).quotaExhaustedis not touched. fix(crawl): exit 1 when a crawl stops because credits ran out #49'sstatus: nullchange is not needed: the run-failure error sets its ownlikely_cause.Testing
npm run typechecknpm test: 411 pass. The newtests/batch-consistency.test.tshas 40 tests using the API's wire shapes. The/stopfixture now returns a run.npm run build, then every subcommand fromdist/against a local mock of the Batch API: estimate, create (201, 202, 503 then success with the same key, 503 exhausted, 402, 402 expired, 429), list, status, wait (timeout, paused, Ctrl-C exits 130), results (partial,--run,--downloadwith expired rows), content (200, 409, 410, 422), cancel (running, ended), pause (running, ended), resume, rerun and retry-failed (default,--all,--status,--allwith--status,--include-pending, each 409), and delete (with and without--yes).There is no
lintscript in this repo.