{"record":{"id":"cbd4e4c95b27eef2","repo":"tinyhumansai/openhuman","slug":"learning-forget-facet-upsert-failed-e","errorCode":null,"errorMessage":"learning_forget_facet: upsert failed: {e:#}","messagePattern":"learning_forget_facet: upsert failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/learning/tools.rs","lineNumber":391,"sourceCode":"        PermissionLevel::Write\n    }\n\n    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {\n        log::debug!(\"[tool][learning] forget_facet invoked\");\n        let class_str = read_required_str(&args, \"class\")?;\n        let key_suffix = read_required_str(&args, \"key\")?;\n        let fk = full_key(&class_str, &key_suffix);\n        let cache = get_cache()?;\n        let facet_json = match cache\n            .get(&fk)\n            .map_err(|e| anyhow::anyhow!(\"learning_forget_facet: {e:#}\"))?\n        {\n            Some(mut f) => {\n                f.user_state = UserState::Forgotten;\n                f.state = FacetState::Dropped;\n                cache\n                    .upsert(&f)\n                    .map_err(|e| anyhow::anyhow!(\"learning_forget_facet: upsert failed: {e:#}\"))?;\n                facet_to_json(&f)\n            }\n            None => serde_json::Value::Null,\n        };\n        Ok(ToolResult::success(serde_json::to_string(&json!({\n            \"facet\": facet_json,\n        }))?))\n    }\n}\n\n/// Rebuild the facet cache (heavyweight stability cycle). Default-OFF.\npub struct LearningRebuildCacheTool;\n\n#[async_trait]\nimpl Tool for LearningRebuildCacheTool {\n    fn name(&self) -> &str {\n        \"learning_rebuild_cache\"\n    }","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/learning/tools.rs#L373-L409","documentation":"Thrown by learning_forget_facet when the facet was found and mutated in memory (user_state=Forgotten, state=Dropped) but FacetCache::upsert() failed. Consequence to understand: the forget did NOT persist — the facet remains active/pinned and will keep appearing in prompts until a successful retry.","triggerScenarios":"Forget racing learning_rebuild_cache or another writer holding the SQLite lock; disk full; read-only DB. The read succeeded moments earlier, so this is a write-time failure.","commonSituations":"User asks to 'stop remembering X' right as a scheduled reflection persist runs; the retry is then skipped because the tool reported failure and the caller assumes it worked.","solutions":["Retry the forget once the lock clears and confirm the facet now shows user_state 'forgotten' via learning_get_facet","Serialize forget/rebuild/reset calls — they all write the same table","Check disk space / single-core-per-workspace if it recurs"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = tool_exec(\"learning_forget_facet\", args).await {\n    if e.to_string().contains(\"upsert failed\") {\n        tokio::time::sleep(Duration::from_millis(500)).await;\n        tool_exec(\"learning_forget_facet\", args).await?; // retry\n        // then confirm: learning_get_facet -> user_state == forgotten\n    }\n}","preventionTips":["Never assume a failed forget worked — an un-persisted forgotten facet keeps influencing prompts","Verify with learning_get_facet (user_state 'forgotten', state 'dropped') after retrying"],"tags":["rust","sqlite","learning","agent-tools","forget","write-path"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}