{"record":{"id":"16ce4363f8e1c94f","repo":"tinyhumansai/openhuman","slug":"failed-to-read-config-file-ownership","errorCode":null,"errorMessage":"Failed to read config file: {}{ownership}","messagePattern":"Failed to read config file: (.+?)(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/config/schema/load/impl_load.rs","lineNumber":42,"sourceCode":"\n/// Try to read `config_path`. On content corruption (non-UTF-8 bytes), rename\n/// the corrupted file to `<config_file>.corrupted.<timestamp>`, try the `.bak`\n/// backup, and if that also fails return an empty string so the caller's\n/// `parse_config_with_recovery` falls through to defaults.\n///\n/// Only triggers auto-recovery for **content** corruption (`InvalidData`), not\n/// for transient or permission errors (`PermissionDenied`, `NotFound`, etc.),\n/// which are propagated as errors so the caller can surface them to the user.\n///\n/// Rate-limits the warning to at most one per process lifetime so a\n/// permanently corrupted file does not flood telemetry (#5167).\nasync fn read_config_with_recovery_or_default(config_path: &Path) -> Result<(String, bool)> {\n    let reads = || async {\n        match fs::read_to_string(config_path).await {\n            Ok(contents) => Ok(contents),\n            Err(error) => {\n                let ownership = describe_config_ownership(config_path).await;\n                Err(anyhow::Error::new(error).context(format!(\n                    \"Failed to read config file: {}{ownership}\",\n                    config_path.display()\n                )))\n            }\n        }\n    };\n\n    let result =\n        crate::openhuman::util::retry_with_backoff_async(\"read config file\", 5, 20, reads).await;\n    match result {\n        Ok(contents) => Ok((contents, false)),\n        Err(e) => {\n            // Check if this is a content-corruption error (non-UTF-8).\n            // Only in that case do we auto-recover by renaming the file\n            // and falling back to backup/defaults. Other errors (permission\n            // denied, file not found after retries, etc.) are propagated\n            // so the caller surfaces them to the user.\n            let is_content_corruption = e.chain().any(|cause| {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/config/schema/load/impl_load.rs#L24-L60","documentation":"config_path could not be read for a reason that is deliberately NOT auto-recovered: unlike content corruption (InvalidData, which triggers rename-to-.corrupted plus .bak fallback plus defaults), transient/permission errors such as PermissionDenied or NotFound are propagated to the caller so the user sees them. The {ownership} suffix annotates file-ownership context. Returning an empty string here would silently mask a permissions problem as 'no config', so the read error is surfaced.","triggerScenarios":"Thrown at src/openhuman/config/schema/load/impl_load.rs:42 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Check the file permissions/ownership of the config file and correct them (chmod/chown) so the process can read it","Verify config_path actually exists and the parent directories are accessible","If the error is transient (e.g. disk/IO), retry starting the core once the underlying issue clears"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}