{"record":{"id":"6585fbc6f7f7e841","repo":"tinyhumansai/openhuman","slug":"learning-save-profile-e","errorCode":null,"errorMessage":"learning_save_profile: {e}","messagePattern":"learning_save_profile: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/learning/tools.rs","lineNumber":530,"sourceCode":"            },\n            \"required\": [\"markdown\"]\n        })\n    }\n\n    fn permission_level(&self) -> PermissionLevel {\n        PermissionLevel::Write\n    }\n\n    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {\n        log::debug!(\"[tool][learning] save_profile invoked\");\n        let markdown = read_required_str(&args, \"markdown\")?;\n        let summarize = args\n            .get(\"summarize\")\n            .and_then(serde_json::Value::as_bool)\n            .unwrap_or(false);\n        let config = config_rpc::load_config_with_timeout()\n            .await\n            .map_err(|e| anyhow::anyhow!(\"learning_save_profile: {e}\"))?;\n        let body = if summarize {\n            crate::openhuman::agent::learning::linkedin_enrichment::summarise_profile_with_llm(\n                &config, &markdown,\n            )\n            .await\n            .map_err(|e| anyhow::anyhow!(\"learning_save_profile: summarisation failed: {e:#}\"))?\n        } else {\n            markdown\n        };\n        let path = config.workspace_dir.join(\"PROFILE.md\");\n        if let Some(parent) = path.parent() {\n            tokio::fs::create_dir_all(parent)\n                .await\n                .map_err(|e| anyhow::anyhow!(\"learning_save_profile: create dir failed: {e}\"))?;\n        }\n        tokio::fs::write(&path, &body)\n            .await\n            .map_err(|e| anyhow::anyhow!(\"learning_save_profile: write failed: {e}\"))?;","sourceCodeStart":512,"sourceCodeEnd":548,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/learning/tools.rs#L512-L548","documentation":"Thrown by learning_save_profile when config_rpc::load_config_with_timeout() fails before the markdown can be written. That helper loads the TOML Config (with env overrides) under a bounded deadline inside the process; failure means the config file was unreadable/invalid or the load exceeded its timeout (Display-only {e}, no error chain). Without a Config the tool cannot resolve workspace_dir for PROFILE.md.","triggerScenarios":"Hand-edited config.toml with syntax errors or invalid typed fields; config load blocked behind a contended lock until the timeout fired; workspace/config directory missing or unreadable; env override pointing at a bad path.","commonSituations":"User edits config.toml and leaves a typo, then asks the agent to save their profile; slow/locked filesystem (network home dir) blowing the load timeout; fresh machine without an initialized workspace.","solutions":["Validate config.toml parses (openhuman config RPC get, or re-run the core) and fix reported syntax/type errors","Retry once filesystem contention clears — the timeout variant fails transiently under lock pressure","Confirm the workspace directory exists and the user has write access","Check OPENHUMAN_* env overrides for a misrouted workspace path"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Verify config loads before invoking the tool:\n// via RPC: config.get (any read) — if the config is broken this fails the same way\n// in-process: config_rpc::load_config_with_timeout().await.is_ok()","typeGuard":null,"tryCatchPattern":"match tool_exec(\"learning_save_profile\", args).await {\n    Err(e) if e.to_string().starts_with(\"learning_save_profile:\") && !e.to_string().contains(\"failed\") => {\n        // config-load failure: fix config.toml first, then retry — no partial writes happened\n        Err(e)\n    }\n    other => other,\n}","preventionTips":["Validate config.toml after every manual edit (the core logs parse errors at startup)","Keep the workspace on local disk — network home dirs blow the load timeout","A failed config load aborts before any file write, so a plain retry after fixing config is safe"],"tags":["rust","config","toml","learning","timeout"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}