{"record":{"id":"1f452668d790a3c7","repo":"tinyhumansai/openhuman","slug":"tool-re-read-failed-e","errorCode":null,"errorMessage":"{tool}: re-read failed: {e:#}","messagePattern":"(.+?): re-read failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/learning/tools.rs","lineNumber":279,"sourceCode":"/// Set/clear a facet's pin via `set_user_state`. Shared by pin/unpin.\nasync fn set_pin(\n    args: serde_json::Value,\n    tool: &str,\n    state: UserState,\n) -> anyhow::Result<ToolResult> {\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 updated = cache\n        .set_user_state(&fk, state)\n        .map_err(|e| anyhow::anyhow!(\"{tool}: set_user_state failed: {e:#}\"))?;\n    if !updated {\n        return Err(anyhow::anyhow!(\"{tool}: facet not found: {fk}\"));\n    }\n    let facet = cache\n        .get(&fk)\n        .map_err(|e| anyhow::anyhow!(\"{tool}: re-read failed: {e:#}\"))?;\n    Ok(ToolResult::success(serde_json::to_string(&json!({\n        \"facet\": facet.as_ref().map(facet_to_json),\n    }))?))\n}\n\n/// Pin a facet. Default-OFF.\npub struct LearningPinFacetTool;\n\n#[async_trait]\nimpl Tool for LearningPinFacetTool {\n    fn name(&self) -> &str {\n        \"learning_pin_facet\"\n    }\n\n    fn description(&self) -> &str {\n        \"Pin a learned facet (`class` + `key`) so it stays active regardless of \\\n         the stability detector.\"\n    }","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/learning/tools.rs#L261-L297","documentation":"Thrown by set_pin when the pin/unpin UPDATE succeeded but the immediate re-read (FacetCache::get) of the same key failed. A rare storage failure between two statements in one tool call: the pin IS persisted, only the confirmation read failed. Same causes as the other store errors — lock transition, I/O error, DB closing underneath.","triggerScenarios":"Another connection takes the write lock between the UPDATE and the SELECT (e.g. a rebuild cycle starting mid-call); the memory client's store being shut down concurrently (core shutting down mid-tool); disk I/O error.","commonSituations":"Core shutdown or restart racing an in-flight pin tool call; aggressive scheduled stability cycles in small workspaces.","solutions":["Treat the pin as probably applied — verify with learning_get_facet instead of blindly re-pinning","Retry the whole pin/unpin if verification shows the old user_state — the operation is idempotent","Avoid issuing learning tools while the core is restarting"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match tool_exec(&tool_name, args).await {\n    Err(e) if e.to_string().contains(\"re-read failed\") => {\n        // the pin itself likely landed — confirm instead of blind retry\n        tool_exec(\"learning_get_facet\", args).await // inspect user_state\n    }\n    other => other,\n}","preventionTips":["On re-read failure, verify the applied user_state with learning_get_facet before retrying the pin","Don't fire learning tools while the core is shutting down or restarting"],"tags":["rust","sqlite","learning","agent-tools","race-condition"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}