{"record":{"id":"895faee2da52331c","repo":"unclecode/crawl4ai","slug":"rejected-request-e","errorCode":null,"errorMessage":"Rejected request: {e}","messagePattern":"Rejected request: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"deploy/docker/api.py","lineNumber":813,"sourceCode":"            pass\n\n        # Add hooks information if hooks were used\n        if hooks_config:\n            response[\"hooks\"] = hooks_status\n\n        return response\n\n    except (UntrustedConfigError, HookValidationError) as e:\n        # An untrusted request body tried to set a forbidden power-field,\n        # construct a disallowed type, or specify an invalid hook. Client error.\n        try:\n            from monitor import get_monitor\n            await get_monitor().track_request_end(\n                request_id, success=False, error=str(e), status_code=400\n            )\n        except:\n            pass\n        raise HTTPException(status_code=400, detail=f\"Rejected request: {e}\")\n\n    except asyncio.TimeoutError:\n        # Per-crawl wall-clock deadline exceeded.\n        raise HTTPException(status_code=504, detail=\"Crawl exceeded the time limit\")\n\n    except HTTPException:\n        # Deliberate status (e.g. 400 SSRF \"URL blocked\") must pass through\n        # rather than be genericized to 500 by the handler below.\n        raise\n\n    except Exception as e:\n        logger.error(f\"Crawl error: {str(e)}\", exc_info=True)\n\n        # Track request error\n        try:\n            from monitor import get_monitor\n            await get_monitor().track_request_end(\n                request_id, success=False, error=str(e), status_code=500","sourceCodeStart":795,"sourceCodeEnd":831,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/api.py#L795-L831","documentation":"HTTP 400 'Rejected request: {e}' from the main crawl handler (deploy/docker/api.py:813): the request body contained config that tripped server-side trust validation — UntrustedConfigError (client tried to set forbidden power-fields like cookies/storage_state/headers beyond policy, or construct disallowed types) or HookValidationError (declarative hook spec with unknown action or invalid params). The monitor records the rejection; the message names the offending item.","triggerScenarios":"POST /crawl with a crawler_config JSON that includes fields on the server's forbidden list (e.g. browser-level extra_args, proxy settings, storage_state); hooks spec using an action name the server does not whitelist; wrong param types in a hook action (string where int required).","commonSituations":"Porting a working local crawl4ai CrawlerRunConfig verbatim into the Docker API body; newer client sending config fields this (older) server build forbids; probing users testing what the sandbox permits.","solutions":["Read detail — it includes the specific exception text naming the forbidden field or invalid hook.","Strip power-fields from the request; set them server-side in the trusted config instead.","Check the server's declarative-hook schema for allowed actions/params and conform the hooks block.","Align client and server versions so accepted config fields match."],"exampleFix":"# before\nbody = {\"url\": u, \"crawler_config\": {\"cookies\": cookies, \"verbose\": True}}\n\n# after\nbody = {\"url\": u, \"crawler_config\": {\"verbose\": True}}  # cookies set via trusted server config\nr = client.post(\"/crawl\", json=body)\nif r.status_code == 400:\n    print(r.json()[\"detail\"])  # names the forbidden field","handlingStrategy":"validation","validationCode":"FORBIDDEN_FIELDS = {\"cookies\", \"storage_state\", \"proxy\", \"user_agent\", \"extra_args\"}\n\ndef crawl_body_is_safe(body: dict) -> bool:\n    cfg = body.get(\"crawler_config\", {})\n    if FORBIDDEN_FIELDS & set(cfg.get(\"browser\", {})):\n        return False\n    if FORBIDDEN_FIELDS & set(cfg):\n        return False\n    return all(h.get(\"action\") in ALLOWED_HOOK_ACTIONS for h in body.get(\"hooks\", {}).get(\"hooks\", []))","typeGuard":null,"tryCatchPattern":"r = await client.post(\"/crawl\", json=body)\nif r.status_code == 400 and \"Rejected request\" in r.json().get(\"detail\", \"\"):\n    detail = r.json()[\"detail\"]\n    strip_forbidden(body, detail)   # parse named field from message, remove, resubmit once\n    r = await client.post(\"/crawl\", json=body)","preventionTips":["Send only policy-allowed config fields; keep privileged settings in server config","Validate hook specs against the server's allowed action list before submit","Pin client version to server version to keep accepted fields in sync"],"tags":["http-400","config-validation","sandbox","hooks","security"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}