datawhalechina/hello-agents · warning · HTTPException

配置必须是 JSON 对象

Error message

配置必须是 JSON 对象

What it means

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.

Solutions

  1. Wrap the payload in curly braces so the top-level value is an object: {"llm": {...}}
  2. Check the first non-whitespace character of the content is '{' before submitting
  3. Diff against the existing config.json (GET /config/CONFIG) to confirm the expected shape

Example fix

// before
content = JSON.stringify(["llm", {model_id: "gpt-4", api_key: "sk-...", base_url: "https://api.openai.com/v1"}])
// after
content = JSON.stringify({llm: {model_id: "gpt-4", api_key: "sk-...", base_url: "https://api.openai.com/v1"}})
Defensive patterns

Strategy: validation

Validate before calling

const parsed = JSON.parse(textareaValue);
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
  showError('Top-level value must be a JSON object');
}

Type guard

const isJsonObject = (v: unknown): v is Record<string, unknown> =>
  typeof v === 'object' && v !== null && !Array.isArray(v);

Prevention

When it happens

Trigger: PUT /api/config/CONFIG with content like '["llm", {...}]', '"just a string"', '123', 'true', or 'null'. All parse cleanly but are not dicts.

Common situations: 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).

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/08ade99ff96ce9e6. Report an issue: GitHub.

Appendix: source

Thrown at Co-creation-projects/tino-chen-HelloClaw/src/api/config.py:98

        raise HTTPException(status_code=404, detail=f"配置文件 {name} 不存在")
    return {"name": name, "content": content}


@router.put("/{name}")
async def update_config(name: str, request: ConfigUpdateRequest, ws: WorkspaceManager = Depends(get_workspace)):
    """更新配置文件"""
    # 特殊处理 CONFIG (config.json)
    if name == "CONFIG":
        ensure_config_json_exists()
        # 严格校验 JSON 格式
        try:
            config_data = json.loads(request.content)
        except json.JSONDecodeError as e:
            raise HTTPException(status_code=400, detail=f"无效的 JSON 格式: {str(e)}")

        # 校验必需字段
        if not isinstance(config_data, dict):
            raise HTTPException(status_code=400, detail="配置必须是 JSON 对象")

        if "llm" not in config_data:
            raise HTTPException(status_code=400, detail="缺少必需字段: llm")

        llm_config = config_data.get("llm", {})
        required_fields = ["model_id", "api_key", "base_url"]
        missing_fields = [f for f in required_fields if f not in llm_config]
        if missing_fields:
            raise HTTPException(status_code=400, detail=f"llm 配置缺少必需字段: {', '.join(missing_fields)}")

        config_path = get_config_json_path()
        with open(config_path, "w", encoding="utf-8") as f:
            f.write(request.content)
        return {"name": name, "status": "updated"}

    # 处理 .md 配置文件
    if name not in ws.list_configs():
        raise HTTPException(status_code=404, detail=f"配置文件 {name} 不存在")

View on GitHub (pinned to 606a07d341)