{"record":{"id":"f5280e3ffbaf218e","repo":"datawhalechina/hello-agents","slug":"llm-join-missing-fields","errorCode":null,"errorMessage":"llm 配置缺少必需字段: {', '.join(missing_fields)}","messagePattern":"llm 配置缺少必需字段: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"Co-creation-projects/tino-chen-HelloClaw/src/api/config.py","lineNumber":107,"sourceCode":"        ensure_config_json_exists()\n        # 严格校验 JSON 格式\n        try:\n            config_data = json.loads(request.content)\n        except json.JSONDecodeError as e:\n            raise HTTPException(status_code=400, detail=f\"无效的 JSON 格式: {str(e)}\")\n\n        # 校验必需字段\n        if not isinstance(config_data, dict):\n            raise HTTPException(status_code=400, detail=\"配置必须是 JSON 对象\")\n\n        if \"llm\" not in config_data:\n            raise HTTPException(status_code=400, detail=\"缺少必需字段: llm\")\n\n        llm_config = config_data.get(\"llm\", {})\n        required_fields = [\"model_id\", \"api_key\", \"base_url\"]\n        missing_fields = [f for f in required_fields if f not in llm_config]\n        if missing_fields:\n            raise HTTPException(status_code=400, detail=f\"llm 配置缺少必需字段: {', '.join(missing_fields)}\")\n\n        config_path = get_config_json_path()\n        with open(config_path, \"w\", encoding=\"utf-8\") as f:\n            f.write(request.content)\n        return {\"name\": name, \"status\": \"updated\"}\n\n    # 处理 .md 配置文件\n    if name not in ws.list_configs():\n        raise HTTPException(status_code=404, detail=f\"配置文件 {name} 不存在\")\n\n    ws.save_config(name, request.content)\n    return {\"name\": name, \"status\": \"updated\"}\n\n\ndef get_agent():\n    \"\"\"获取全局 Agent 实例\"\"\"\n    from ..main import get_agent as _get_agent\n    return _get_agent()","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/tino-chen-HelloClaw/src/api/config.py#L89-L125","documentation":"Raised by PUT /config/CONFIG when the llm object is present but is missing one or more of model_id, api_key, base_url. The endpoint lists the absent fields in the message (e.g. 'llm 配置缺少必需字段: api_key, base_url'), so the exact gaps are named. HTTP 400; nothing is written to disk.","triggerScenarios":"PUT /api/config/CONFIG with llm = {\"model_id\": \"gpt-4\"} (missing api_key/base_url), or llm set to an empty object, or fields misspelled (\"apiKey\", \"baseUrl\", \"model\").","commonSituations":"Migrating from an older config schema that used different key names; user intentionally blanks the api_key for a local/Ollama model not realizing base_url is still mandatory; frontend form submits only dirty fields.","solutions":["Add every field named in the error message under llm: model_id, api_key, base_url","Verify exact snake_case spellings — apiKey/baseUrl/model are not accepted","For local backends set base_url to the local endpoint (e.g. http://localhost:11434/v1) and a non-empty placeholder api_key if required"],"exampleFix":"// before\n{llm: {model_id: \"gpt-4\", apiKey: \"sk-...\"}}\n// after\n{llm: {model_id: \"gpt-4\", api_key: \"sk-...\", base_url: \"https://api.openai.com/v1\"}}","handlingStrategy":"validation","validationCode":"const REQUIRED = ['model_id', 'api_key', 'base_url'];\nconst missing = REQUIRED.filter(k => !(k in parsed.llm));\nif (missing.length) showError(`llm missing: ${missing.join(', ')}`);","typeGuard":"const isValidLlm = (v: unknown): v is { model_id: string; api_key: string; base_url: string } =>\n  typeof v === 'object' && v !== null &&\n  ['model_id','api_key','base_url'].every(k => k in v);","tryCatchPattern":null,"preventionTips":["Use a typed config form that cannot submit without the three fields","Keep a known-good config.json backup to restore from"],"tags":["fastapi","json","config","validation","llm","http-400"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}