{"record":{"id":"12259cbc417c6546","repo":"tinyhumansai/openhuman","slug":"learning-save-profile-create-dir-failed-e","errorCode":null,"errorMessage":"learning_save_profile: create dir failed: {e}","messagePattern":"learning_save_profile: create dir failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/learning/tools.rs","lineNumber":544,"sourceCode":"            .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}\"))?;\n        Ok(ToolResult::success(serde_json::to_string(&json!({\n            \"path\": path.display().to_string(),\n            \"bytes\": body.len(),\n        }))?))\n    }\n}\n\n/// Enrich the profile via LinkedIn (external scrape). Default-OFF.\npub struct LearningEnrichProfileTool;\n\n#[async_trait]\nimpl Tool for LearningEnrichProfileTool {\n    fn name(&self) -> &str {\n        \"learning_enrich_profile\"","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/learning/tools.rs#L526-L562","documentation":"Thrown by learning_save_profile when tokio::fs::create_dir_all() on the parent of {workspace_dir}/PROFILE.md fails. Practically that means creating the workspace directory itself; create_dir_all fails on permission denial, a path component existing as a regular file, a read-only filesystem, or I/O errors. Display-only {e} carries the std::io::Error.","triggerScenarios":"workspace_dir resolving to a protected path (permissions changed, different user owning the dir); a stale file occupying a directory component of the path (e.g. 'workspace' existing as a file); read-only mount; workspace on an unmounted/removed volume.","commonSituations":"Workspace directory deleted manually while the core runs; migrating machines with copied home dirs that lost ownership; OPENHUMAN_WORKSPACE override pointing somewhere unwritable.","solutions":["Check the path in the error: ensure every component is a directory the process can write (ls -ld, fix ownership/permissions)","Remove/rename any regular file squatting on a needed directory component","Verify the volume is mounted and not read-only","Confirm the config's workspace_dir setting / env override points where you expect"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// In-process check the tool's own precondition:\nlet cfg = config_rpc::load_config_with_timeout().await?;\nlet dir = cfg.workspace_dir.clone();\nstd::fs::create_dir_all(&dir)?; // surface EACCES/EEXIST-as-file before invoking the tool","typeGuard":null,"tryCatchPattern":"match tool_exec(\"learning_save_profile\", args).await {\n    Err(e) if e.to_string().contains(\"create dir failed\") => {\n        // inspect {e}: EACCES -> fix ownership; EEXIST/ENOTDIR -> a file occupies a path component\n        Err(e)\n    }\n    other => other,\n}","preventionTips":["Keep workspace_dir on a writable local volume owned by the running user","Never let a regular file shadow a directory component of the workspace path","Don't run the core as root and then as a user against the same workspace — ownership flips break creates"],"tags":["rust","filesystem","permissions","learning","profile"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}