{"record":{"id":"63b60bbed7f7a061","repo":"datawhalechina/hello-agents","slug":"str-exc","errorCode":null,"errorMessage":"{str(exc)}","messagePattern":"\\{str\\(exc\\)\\}","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"code/chapter14/helloagents-deepresearch/backend/src/main.py","lineNumber":125,"sourceCode":"            config.max_web_research_loops,\n            config.fetch_full_page,\n            config.use_tool_calling,\n            config.strip_thinking_tokens,\n            _mask_secret(config.llm_api_key),\n        )\n\n    @app.get(\"/healthz\")\n    def health_check() -> Dict[str, str]:\n        return {\"status\": \"ok\"}\n\n    @app.post(\"/research\", response_model=ResearchResponse)\n    def run_research(payload: ResearchRequest) -> ResearchResponse:\n        try:\n            config = _build_config(payload)\n            agent = DeepResearchAgent(config=config)\n            result = agent.run(payload.topic)\n        except ValueError as exc:  # Likely due to unsupported configuration\n            raise HTTPException(status_code=400, detail=str(exc)) from exc\n        except Exception as exc:  # pragma: no cover - defensive guardrail\n            raise HTTPException(status_code=500, detail=\"Research failed\") from exc\n\n        todo_payload = [\n            {\n                \"id\": item.id,\n                \"title\": item.title,\n                \"intent\": item.intent,\n                \"query\": item.query,\n                \"status\": item.status,\n                \"summary\": item.summary,\n                \"sources_summary\": item.sources_summary,\n                \"note_id\": item.note_id,\n                \"note_path\": item.note_path,\n            }\n            for item in result.todo_items\n        ]\n","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/code/chapter14/helloagents-deepresearch/backend/src/main.py#L107-L143","documentation":"HTTPException(400) raised by POST /research in the chapter14 deep-research backend when _build_config(payload) or the agent raises ValueError — the route deliberately maps ValueError to a client-error 400 with the original message. It marks configuration the request asked for as unsupported (bad model name, bad provider, invalid search settings) rather than a server fault.","triggerScenarios":"Payload specifying an unknown/unsupported model or provider; malformed enum-ish fields (e.g. invalid search depth or max iterations as strings) that _build_config parses and rejects; empty/whitespace topic triggering validation ValueError.","commonSituations":"Frontend sending a model id removed after a backend update; user-typed config fields passed straight through; API consumers guessing field values instead of following the schema.","solutions":["Read the detail field — it is the original ValueError text naming the unsupported value","Fix the request payload (correct model/provider names, valid numeric fields) per the ResearchRequest schema","If the value should be supported, extend _build_config's allowlist rather than catching the 400"],"exampleFix":"# before\npayload = {\"topic\": \"quantum computing\", \"model\": \"gpt-99\"}  # -> 400\n\n# after\npayload = {\"topic\": \"quantum computing\", \"model\": \"deepseek-chat\"}  # a supported id\nr = requests.post(f'{BASE}/research', json=payload)\nif r.status_code == 400:\n    print('Bad request config:', r.json()['detail'])","handlingStrategy":"validation","validationCode":"SUPPORTED_MODELS = {'deepseek-chat', 'deepseek-reasoner', ...}  # mirror _build_config\n\ndef valid_research_payload(p: dict) -> bool:\n    return (\n        bool(str(p.get('topic', '')).strip())\n        and p.get('model', 'default') in SUPPORTED_MODELS\n    )","typeGuard":"def is_supported_model(m) -> bool:\n    return isinstance(m, str) and m in SUPPORTED_MODELS","tryCatchPattern":"r = requests.post(f'{BASE}/research', json=payload)\nif r.status_code == 400:\n    raise ValueError(f'Unsupported research config: {r.json()[\"detail\"]}')  # actionable message\nr.raise_for_status()","preventionTips":["Publish the supported model/provider list in the API docs and validate client-side","Pass through the 400 detail to users — it names the exact unsupported value","Add schema-level enums to ResearchRequest so bad values fail at validation, not inside _build_config"],"tags":["fastapi","http-400","validation","configuration"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}