{"record":{"id":"67b8a410fc3e7981","repo":"datawhalechina/hello-agents","slug":"request-category-valid-categories","errorCode":null,"errorMessage":"无效的分类: {request.category}，有效值: {valid_categories}","messagePattern":"无效的分类: (.+?)，有效值: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"Co-creation-projects/tino-chen-HelloClaw/src/api/memory.py","lineNumber":196,"sourceCode":"\n\n@router.post(\"/today\")\nasync def add_to_today(content: str, ws: WorkspaceManager = Depends(get_workspace)):\n    \"\"\"添加内容到今日记忆\"\"\"\n    ws.append_to_daily_memory(content)\n    return {\"status\": \"ok\", \"message\": \"已添加到今日记忆\"}\n\n\n@router.post(\"/capture\", response_model=MemoryCaptureResponse)\nasync def capture_memory(\n    request: MemoryCaptureRequest,\n    ws: WorkspaceManager = Depends(get_workspace)\n):\n    \"\"\"手动添加记忆（带分类）\"\"\"\n    # 验证分类\n    valid_categories = [\"preference\", \"decision\", \"entity\", \"fact\"]\n    if request.category not in valid_categories:\n        raise HTTPException(\n            status_code=400,\n            detail=f\"无效的分类: {request.category}，有效值: {valid_categories}\"\n        )\n\n    # 检查重复\n    if ws.check_duplicate_memory(request.content, threshold=0.7):\n        return MemoryCaptureResponse(\n            status=\"skipped\",\n            message=\"记忆已存在，跳过\",\n            category=request.category\n        )\n\n    # 存储记忆\n    ws.append_classified_memory(request.content, request.category)\n\n    return MemoryCaptureResponse(\n        status=\"ok\",\n        message=f\"已添加 [{request.category}] 记忆\",","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/tino-chen-HelloClaw/src/api/memory.py#L178-L214","documentation":"Raised by POST /api/memory/capture when request.category is not one of the four allowed strings: preference, decision, entity, fact. The whitelist is hardcoded in the route (not in the Pydantic model), so any other value — including differently-cased forms — is rejected with HTTP 400 listing the valid values.","triggerScenarios":"POST /api/memory/capture with category 'Preference' (capitalized), 'note', 'idea', 'snippet', or a null/omitted category field.","commonSituations":"Client UI dropdown that grew new categories without updating the API whitelist; enum serialized as 'MemoryCategory.preference' instead of the bare value; copy-pasted category names from other tools (Notion/Readwise tags).","solutions":["Send one of exactly: preference, decision, entity, fact — all lowercase","Constrain the client field to a closed enum so invalid values cannot be constructed","If a new category is genuinely needed, extend the valid_categories list in memory.py (server change)"],"exampleFix":"// before\nbody = {content: \"...\", category: \"Preference\"}\n// after\nconst CATEGORIES = ['preference','decision','entity','fact'];\nbody = {content: \"...\", category: CATEGORIES.includes(category) ? category : 'fact'}","handlingStrategy":"validation","validationCode":"const VALID = new Set(['preference', 'decision', 'entity', 'fact']);\nconst category = VALID.has(raw) ? raw : null;\nif (!category) throw new Error(`Invalid category: ${raw}`);","typeGuard":"const isMemoryCategory = (v: string): v is 'preference'|'decision'|'entity'|'fact' =>\n  ['preference','decision','entity','fact'].includes(v);","tryCatchPattern":null,"preventionTips":["Model the field as a closed union type in the client","Lowercase/trim category input before sending"],"tags":["fastapi","memory","validation","http-400"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}