{"record":{"id":"08ade99ff96ce9e6","repo":"datawhalechina/hello-agents","slug":"json","errorCode":null,"errorMessage":"配置必须是 JSON 对象","messagePattern":"配置必须是 JSON 对象","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"Co-creation-projects/tino-chen-HelloClaw/src/api/config.py","lineNumber":98,"sourceCode":"        raise HTTPException(status_code=404, detail=f\"配置文件 {name} 不存在\")\n    return {\"name\": name, \"content\": content}\n\n\n@router.put(\"/{name}\")\nasync def update_config(name: str, request: ConfigUpdateRequest, ws: WorkspaceManager = Depends(get_workspace)):\n    \"\"\"更新配置文件\"\"\"\n    # 特殊处理 CONFIG (config.json)\n    if name == \"CONFIG\":\n        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} 不存在\")","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/tino-chen-HelloClaw/src/api/config.py#L80-L116","documentation":"Raised by PUT /config/CONFIG when the submitted content parses as valid JSON but is not an object at the top level (json.loads succeeded, isinstance(config_data, dict) failed). The stored config.json must be a mapping because the code indexes 'llm' into it, so arrays or scalars are rejected with HTTP 400 before the file is written.","triggerScenarios":"PUT /api/config/CONFIG with content like '[\"llm\", {...}]', '\"just a string\"', '123', 'true', or 'null'. All parse cleanly but are not dicts.","commonSituations":"User wraps the config in an array thinking it is a list format; user saves a bare API-key string instead of the full object; frontend serializes the wrong variable (a value instead of the containing object).","solutions":["Wrap the payload in curly braces so the top-level value is an object: {\"llm\": {...}}","Check the first non-whitespace character of the content is '{' before submitting","Diff against the existing config.json (GET /config/CONFIG) to confirm the expected shape"],"exampleFix":"// before\ncontent = JSON.stringify([\"llm\", {model_id: \"gpt-4\", api_key: \"sk-...\", base_url: \"https://api.openai.com/v1\"}])\n// after\ncontent = JSON.stringify({llm: {model_id: \"gpt-4\", api_key: \"sk-...\", base_url: \"https://api.openai.com/v1\"}})","handlingStrategy":"validation","validationCode":"const parsed = JSON.parse(textareaValue);\nif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n  showError('Top-level value must be a JSON object');\n}","typeGuard":"const isJsonObject = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":null,"preventionTips":["Seed the editor with the existing config.json object, not a blank canvas","Reject arrays/scalars client-side with a clear message before PUT"],"tags":["fastapi","json","config","validation","http-400"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}