{"record":{"id":"22215011de4f3c62","repo":"unclecode/crawl4ai","slug":"str-e-222150","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"deploy/docker/server.py","lineNumber":560,"sourceCode":"            \"Token issuance is disabled: no api_token is configured on the server.\",\n        )\n    if not req.api_token or not constant_time_eq(req.api_token, expected_token):\n        raise HTTPException(401, \"Invalid or missing api_token\")\n    if not verify_email_domain(req.email):\n        raise HTTPException(400, \"Invalid email domain\")\n    token = create_access_token({\"sub\": req.email})\n    return {\"email\": req.email, \"access_token\": token, \"token_type\": \"bearer\"}\n\n\n@app.post(\"/config/dump\")\nasync def config_dump(\n    data: dict,\n    _td: Dict = Depends(token_dep),\n):\n    try:\n        return JSONResponse(_config_from_json(data))\n    except (TypeError, ValueError) as e:\n        raise HTTPException(400, str(e))\n\n\n@app.post(\"/md\")\n@limiter.limit(config[\"rate_limiting\"][\"default_limit\"])\n@mcp_tool(\"md\")\nasync def get_markdown(\n    request: Request,\n    body: MarkdownRequest,\n    _td: Dict = Depends(token_dep),\n):\n    \"\"\"\n    Convert a web page into Markdown format.\n\n    Supports multiple extraction modes:\n    - fit (default): Readability-based extraction for clean content\n    - raw: Direct DOM to Markdown conversion\n    - bm25: BM25 relevance ranking with optional query\n    - llm: LLM-based summarization with optional query","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/server.py#L542-L578","documentation":"A 400 raised by POST /config/dump when _config_from_json() raises TypeError or ValueError while parsing the submitted config dict. The detail is the underlying exception message, so the real cause (bad key, wrong type, malformed value) is embedded in str(e).","triggerScenarios":"POST /config/dump with a JSON body that is not a valid CrawlerRunConfig/BrowserConfig shape: unknown/misspelled keys, wrong value types (string where a number is expected), or nested structures that fail pydantic/manual parsing. Auth via token_dep must already have passed.","commonSituations":"Client sends a config dumped from an older/newer crawl4ai version whose schema differs; hand-editing a config dump and introducing typos; passing {'crawler': {...}} vs the flat dict the endpoint expects.","solutions":["Read the str(e) detail in the 400 response — it names the offending key/value.","Validate your payload against the current schema first: GET /schema returns the canonical BrowserConfig/CrawlerRunConfig dump.","Regenerate the config from a working crawl instead of hand-writing it.","Match the crawl4ai version between the client that produced the config and the server that consumes it."],"exampleFix":"# before\nrequests.post(f'{base}/config/dump', json={'browser': {'headless': 'yes'}})  # str instead of bool\n# after\nschema = requests.get(f'{base}/schema').json()\nrequests.post(f'{base}/config/dump', json={'browser': {'headless': True}})","handlingStrategy":"validation","validationCode":"import requests\n\nschema = requests.get(f'{BASE}/schema').json()  # canonical config shape\n\ndef check_config(cfg: dict) -> None:\n    def walk(node, template, path='config'):\n        if isinstance(template, dict):\n            for k, v in node.items():\n                if k not in template:\n                    raise ValueError(f'unknown key {path}.{k}')\n                walk(v, template[k], f'{path}.{k}')\n        elif isinstance(template, bool):\n            if not isinstance(node, bool):\n                raise ValueError(f'{path} must be bool, got {type(node).__name__}')\n    walk(cfg, schema)","typeGuard":null,"tryCatchPattern":"resp = requests.post(f'{BASE}/config/dump', json=cfg, headers=hdrs)\nif resp.status_code == 400:\n    raise ValueError(f'config rejected: {resp.json()[\"detail\"]}')  # detail carries the offending key","preventionTips":["Fetch /schema and validate keys/types before POSTing a config.","Never hand-edit config dumps; derive them from a successful crawl.","Pin the crawl4ai version so config schema doesn't drift under you."],"tags":["config","validation","pydantic","http-400"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}