Hmbown/CodeWhale · error · anyhow::Error

Runtime store path must be a directory: {}

Error message

Runtime store path must be a directory: {}

What it means

After create_dir_all and the symlink check, ensure_runtime_store_dir/reject_symlinked_store_dir found the store path exists but is not a directory (crates/tui/src/runtime_threads.rs:9038) — typically a regular file occupying the name where the directory must live, so directory creation cannot succeed and subsequent file writes would fail.

Source

Thrown at crates/tui/src/runtime_threads.rs:9038

        .set_len(original_len)
        .context("Failed to roll back Runtime event")?;
    rollback_file
        .sync_all()
        .context("Failed to sync Runtime event rollback")
}

fn reject_symlinked_store_dir(path: &Path) -> Result<()> {
    let Ok(metadata) = fs::symlink_metadata(path) else {
        return Ok(());
    };
    if metadata.file_type().is_symlink() {
        bail!(
            "Runtime store directory must not be a symlink: {}",
            path.display()
        );
    }
    if !metadata.is_dir() {
        bail!("Runtime store path must be a directory: {}", path.display());
    }
    Ok(())
}

fn ensure_runtime_store_dir(path: &Path) -> Result<()> {
    fs::create_dir_all(path).with_context(|| format!("Failed to create {}", path.display()))?;
    reject_symlinked_store_dir(path)
}

fn read_complete_event(
    reader: &mut impl BufRead,
    path: &Path,
) -> Result<Option<RuntimeEventRecord>> {
    Ok(read_complete_event_bytes(reader, path)?.map(|(event, _)| event))
}

fn read_complete_event_bytes(
    reader: &mut impl BufRead,

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Remove or rename the conflicting file so a real directory can be created
  2. Choose a different, directory-shaped store root path
  3. Restore from backup if the file was a legitimate store artifact misplaced
  4. Add a startup check that the configured root either does not exist or is a directory

Example fix

# before
$ ls -la ~/.codewhale
-rw-r--r--  1 me  me  4096 runtime   # file blocking the dir

# after
$ mv ~/.codewhale/runtime ~/.codewhale/runtime.bak
$ mkdir -p ~/.codewhale/runtime
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the configured root is absent or a directory before starting.
if let Ok(md) = std::fs::symlink_metadata(&root) {
    if !md.is_dir() {
        return Err(anyhow::anyhow!("store root is not a directory: {}", root.display()));
    }
}

Type guard

fn is_directory_or_missing(path: &Path) -> bool {
    std::fs::symlink_metadata(path)
        .map(|md| md.is_dir())
        .unwrap_or(true)
}

Try / catch

match ensure_runtime_store_dir(&root) {
    Ok(()) => {}
    Err(err) if err.to_string().contains("must be a directory") => {
        // Move the conflicting file aside and let creation proceed.
        std::fs::rename(&root, root.with_extension("conflict"))?;
        ensure_runtime_store_dir(&root)?;
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Store root misconfigured to a path that is an existing file (e.g. pointing at a config file); an earlier partial setup left a file where the directory belongs; a typo maps the store root onto a file like 'runtime.json'

Common situations: Copy-pasted config where a file path was pasted into a directory option; cleanup scripts that replaced dirs with marker files; case-insensitive filesystems colliding names.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/e6e624581e52d6a5. Report an issue: GitHub.