{"record":{"id":"1aa187be577c8ad8","repo":"unclecode/crawl4ai","slug":"str-e-1aa187","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"deploy/docker/job.py","lineNumber":81,"sourceCode":"    crawler_config: Dict = {}\n    webhook_config: Optional[WebhookConfig] = None\n\n\n# ---------- LL​M job ---------------------------------------------------------\n@router.post(\"/llm/job\", status_code=202)\nasync def llm_job_enqueue(\n        payload: LlmJobPayload,\n        background_tasks: BackgroundTasks,\n        request: Request,\n        _td: Optional[Dict] = Depends(_principal_dep),\n):\n    webhook_config = None\n    if payload.webhook_config:\n        from utils import validate_webhook_url\n        try:\n            validate_webhook_url(str(payload.webhook_config.webhook_url))\n        except ValueError as e:\n            raise HTTPException(status_code=400, detail=str(e))\n        webhook_config = payload.webhook_config.model_dump(mode='json')\n\n    return await handle_llm_request(\n        _redis,\n        background_tasks,\n        request,\n        str(payload.url),\n        query=payload.q,\n        schema=payload.schema,\n        cache=payload.cache,\n        config=_config,\n        provider=payload.provider,\n        webhook_config=webhook_config,\n        temperature=payload.temperature,\n        requester=_owner_of(_td),\n        is_admin=_is_admin(_td),\n    )\n","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/job.py#L63-L99","documentation":"POST /llm/job (llm_job_enqueue in deploy/docker/job.py) validates payload.webhook_config.webhook_url via utils.validate_webhook_url; a ValueError becomes HTTPException 400 with the validator's message in detail. This fails fast before the 202 job is accepted, so bad webhook URLs never produce jobs whose completion callback silently fails.","triggerScenarios":"Submitting an LLM extraction job with webhook_config.webhook_url that is not a valid http(s) URL — e.g. missing scheme, ftp:// scheme, a hostname the validator considers internal/unresolvable, or a malformed URL string.","commonSituations":"Webhook set to an internal/staging host (blocked to prevent SSRF); typo like 'hooks.example' without scheme; trailing whitespace or quotes around the URL pasted from a secrets manager; using a localhost webhook in production.","solutions":["Read detail in the 400 response — validate_webhook_url's message states the specific rule violated.","Supply a fully-qualified https:// URL on a public host for webhook_url.","If you don't need callbacks, omit webhook_config entirely.","Re-submit the job after fixing the URL; no partial state is created since validation precedes enqueue."],"exampleFix":"# before\n{\"webhook_config\": {\"webhook_url\": \"hooks.mycompany.internal/jobs\"}}\n\n# after\n{\"webhook_config\": {\"webhook_url\": \"https://hooks.mycompany.com/jobs\"}}","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef webhook_ok(url: str) -> bool:\n    p = urlparse(url)\n    return p.scheme in (\"http\", \"https\") and bool(p.netloc) and \"localhost\" not in p.netloc and not p.hostname.startswith(\"127.\")","typeGuard":"def is_submittable_webhook(url) -> bool:\n    try:\n        p = urlparse(url)\n        return p.scheme == \"https\" and \".\" in (p.hostname or \"\")\n    except Exception:\n        return False","tryCatchPattern":"resp = client.post(\"/llm/job\", json=payload)\nif resp.status_code == 400:\n    fix_webhook_from_detail(resp.json()[\"detail\"])  # message states the violated rule\n    resp = client.post(\"/llm/job\", json=payload)","preventionTips":["Validate webhook URLs with the same rules client-side before submit.","Store one canonical, pre-validated webhook URL in config; never hand-type per request.","Trim whitespace/quotes from URLs copied out of secret managers."],"tags":["http-400","webhook","validation","api"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}