ArchiveBox/ArchiveBox · error · HttpError
Invalid action: {action}
Error message
Invalid action: {action} What it means
patch_crawl (PATCH /api/v1/crawl/{crawl_id}) accepts an optional 'action' field in the JSON body; only 'pause', 'resume', 'unpause', and 'cancel' are supported. Any other value raises HttpError 400 with the invalid action echoed back. This is a strict allowlist so unsupported lifecycle transitions fail fast instead of silently doing nothing.
Source
Thrown at archivebox/api/v1_crawls.py:223
@router.patch("/crawl/{crawl_id}", response=CrawlSchema, url_name="patch_crawl")
def patch_crawl(request: HttpRequest, crawl_id: str, data: CrawlUpdateSchema):
"""Update a crawl (e.g., set status=sealed to cancel queued work)."""
crawl = get_crawl_by_ref(crawl_id)
payload = data.dict(exclude_unset=True)
update_fields = ["modified_at"]
action = payload.pop("action", None)
if action:
if action == "pause":
crawl.pause()
return crawl
if action in ("resume", "unpause"):
crawl.resume()
return crawl
if action == "cancel":
crawl.cancel()
return crawl
raise HttpError(400, f"Invalid action: {action}")
tags = payload.pop("tags", None)
tags_str = payload.pop("tags_str", None)
if tags is not None or tags_str is not None:
crawl.tags_str = ",".join(normalize_tag_list(tags, tags_str or ""))
update_fields.append("tags_str")
if "status" in payload:
if payload["status"] not in Crawl.StatusChoices.values:
raise HttpError(400, f"Invalid status: {payload['status']}")
if payload["status"] == Crawl.StatusChoices.SEALED:
crawl.cancel()
return crawl
crawl.status = payload["status"]
update_fields.append("status")
if "retry_at" in payload:
crawl.retry_at = payload["retry_at"]View on GitHub (pinned to 74564b2822)
Solutions
- Use one of the allowed actions: "pause", "resume", "unpause", or "cancel" (exact lowercase spelling)
- If you wanted to stop queued work permanently, send {"action": "cancel"} or set status to "sealed"
- Remove the 'action' field entirely if you only meant to update tags/status/retry_at fields
Example fix
// before
PATCH /api/v1/crawl/abc123 {"action": "stop"}
// after
PATCH /api/v1/crawl/abc123 {"action": "cancel"} Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_ACTIONS = new Set(['pause', 'resume', 'unpause', 'cancel']);
if (body.action !== undefined && !ALLOWED_ACTIONS.has(body.action)) {
throw new Error(`action must be one of ${[...ALLOWED_ACTIONS]}`);
} Type guard
function isValidCrawlAction(a) {
return ['pause', 'resume', 'unpause', 'cancel'].includes(a);
} Try / catch
try {
await patchCrawl(id, { action });
} catch (e) {
if (e.status === 400 && /Invalid action/.test(e.message)) {
// fall back to { status: 'sealed' } for cancel-like intent
}
} Prevention
- Keep the action strings lowercase and exact: pause, resume, unpause, cancel
- Check the generated API schema/OpenAPI spec for the patch endpoint before wiring a UI
- Map UI verbs like 'stop'/'abort' to 'cancel' in a single place
When it happens
Trigger: PATCH /api/v1/crawl/{crawl_id} with body {"action": "stop"}, {"action": "Pause"} (wrong case), {"action": "start"}, or a typo like {"action": "caancel"}.
Common situations: Clients written against older API versions that allowed different action names; casing/typo mistakes; UIs sending 'restart' or 'abort' expecting them to map to cancel.
Related errors
- str(err)
- Invalid ArchiveResult plugin name
- ArchiveResult output path is required
- ArchiveResult output_json must be valid JSON
- ArchiveResult output_json must be a JSON object
AI-assisted analysis of ArchiveBox/ArchiveBox@74564b2822 (2026-08-28).
Data as JSON: /api/errors/209667f5b6119f0a.
Report an issue: GitHub.