tinyhumansai/openhuman · error

Failed to read config file: {}{ownership}

Error message

Failed to read config file: {}{ownership}

What it means

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.

Source

Thrown at src/openhuman/config/schema/load/impl_load.rs:42

/// Try to read `config_path`. On content corruption (non-UTF-8 bytes), rename
/// the corrupted file to `<config_file>.corrupted.<timestamp>`, try the `.bak`
/// backup, and if that also fails return an empty string so the caller's
/// `parse_config_with_recovery` falls through to defaults.
///
/// Only triggers auto-recovery for **content** corruption (`InvalidData`), not
/// for transient or permission errors (`PermissionDenied`, `NotFound`, etc.),
/// which are propagated as errors so the caller can surface them to the user.
///
/// Rate-limits the warning to at most one per process lifetime so a
/// permanently corrupted file does not flood telemetry (#5167).
async fn read_config_with_recovery_or_default(config_path: &Path) -> Result<(String, bool)> {
    let reads = || async {
        match fs::read_to_string(config_path).await {
            Ok(contents) => Ok(contents),
            Err(error) => {
                let ownership = describe_config_ownership(config_path).await;
                Err(anyhow::Error::new(error).context(format!(
                    "Failed to read config file: {}{ownership}",
                    config_path.display()
                )))
            }
        }
    };

    let result =
        crate::openhuman::util::retry_with_backoff_async("read config file", 5, 20, reads).await;
    match result {
        Ok(contents) => Ok((contents, false)),
        Err(e) => {
            // Check if this is a content-corruption error (non-UTF-8).
            // Only in that case do we auto-recover by renaming the file
            // and falling back to backup/defaults. Other errors (permission
            // denied, file not found after retries, etc.) are propagated
            // so the caller surfaces them to the user.
            let is_content_corruption = e.chain().any(|cause| {

View on GitHub (pinned to 7491200858)

Solutions

  1. Check the file permissions/ownership of the config file and correct them (chmod/chown) so the process can read it
  2. Verify config_path actually exists and the parent directories are accessible
  3. If the error is transient (e.g. disk/IO), retry starting the core once the underlying issue clears
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src/openhuman/config/schema/load/impl_load.rs:42 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/16ce4363f8e1c94f. Report an issue: GitHub.