{"record":{"id":"209667f5b6119f0a","repo":"ArchiveBox/ArchiveBox","slug":"invalid-action-action-209667","errorCode":null,"errorMessage":"Invalid action: {action}","messagePattern":"Invalid action: (.+?)","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"archivebox/api/v1_crawls.py","lineNumber":223,"sourceCode":"@router.patch(\"/crawl/{crawl_id}\", response=CrawlSchema, url_name=\"patch_crawl\")\ndef patch_crawl(request: HttpRequest, crawl_id: str, data: CrawlUpdateSchema):\n    \"\"\"Update a crawl (e.g., set status=sealed to cancel queued work).\"\"\"\n    crawl = get_crawl_by_ref(crawl_id)\n    payload = data.dict(exclude_unset=True)\n    update_fields = [\"modified_at\"]\n\n    action = payload.pop(\"action\", None)\n    if action:\n        if action == \"pause\":\n            crawl.pause()\n            return crawl\n        if action in (\"resume\", \"unpause\"):\n            crawl.resume()\n            return crawl\n        if action == \"cancel\":\n            crawl.cancel()\n            return crawl\n        raise HttpError(400, f\"Invalid action: {action}\")\n\n    tags = payload.pop(\"tags\", None)\n    tags_str = payload.pop(\"tags_str\", None)\n    if tags is not None or tags_str is not None:\n        crawl.tags_str = \",\".join(normalize_tag_list(tags, tags_str or \"\"))\n        update_fields.append(\"tags_str\")\n\n    if \"status\" in payload:\n        if payload[\"status\"] not in Crawl.StatusChoices.values:\n            raise HttpError(400, f\"Invalid status: {payload['status']}\")\n        if payload[\"status\"] == Crawl.StatusChoices.SEALED:\n            crawl.cancel()\n            return crawl\n        crawl.status = payload[\"status\"]\n        update_fields.append(\"status\")\n\n    if \"retry_at\" in payload:\n        crawl.retry_at = payload[\"retry_at\"]","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/ArchiveBox/ArchiveBox/blob/74564b28220090664f919479e82cbf454125fa34/archivebox/api/v1_crawls.py#L205-L241","documentation":"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.","triggerScenarios":"PATCH /api/v1/crawl/{crawl_id} with body {\"action\": \"stop\"}, {\"action\": \"Pause\"} (wrong case), {\"action\": \"start\"}, or a typo like {\"action\": \"caancel\"}.","commonSituations":"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.","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"],"exampleFix":"// before\nPATCH /api/v1/crawl/abc123 {\"action\": \"stop\"}\n// after\nPATCH /api/v1/crawl/abc123 {\"action\": \"cancel\"}","handlingStrategy":"validation","validationCode":"const ALLOWED_ACTIONS = new Set(['pause', 'resume', 'unpause', 'cancel']);\nif (body.action !== undefined && !ALLOWED_ACTIONS.has(body.action)) {\n  throw new Error(`action must be one of ${[...ALLOWED_ACTIONS]}`);\n}","typeGuard":"function isValidCrawlAction(a) {\n  return ['pause', 'resume', 'unpause', 'cancel'].includes(a);\n}","tryCatchPattern":"try {\n  await patchCrawl(id, { action });\n} catch (e) {\n  if (e.status === 400 && /Invalid action/.test(e.message)) {\n    // fall back to { status: 'sealed' } for cancel-like intent\n  }\n}","preventionTips":["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"],"tags":["api","http-400","validation","patch"],"backgroundTag":"invalid-action-value","analyzedSha":"74564b28220090664f919479e82cbf454125fa34","analyzedAt":"2026-08-28T23:56:51.556Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}