Hmbown/CodeWhale · error

skill state lock at must be a regular file

Error message

skill state lock at {} must be a regular file

What it means

On platforms other than Unix and Windows, validate_state_lock checks only that the lock file's metadata reports it is a regular file. If the path resolves to a directory, FIFO, device, or other special file, the ensure! fails. Like its platform-specific siblings, this guards the integrity of the skill state lock.

Solutions

  1. Delete the lock path so the application can recreate it as a regular file.
  2. Verify what exists at the path (`ls -la`) and remove any directory/FIFO/device node found there.
  3. Run on a supported mainstream platform if the target's filesystem semantics are unknown.

Example fix

// before
$ ls ~/.codewhale/skill-state.lock
directory
// after
$ rm -rf ~/.codewhale/skill-state.lock
$ codewhale  # recreates a regular lock file
Defensive patterns

Strategy: validation

Validate before calling

let md = std::fs::metadata(lock_path)?;
if !md.is_file() {
    std::fs::remove_file(lock_path).or_else(|_| std::fs::remove_dir_all(lock_path))?;
}

Type guard

fn is_regular_file(p: &std::path::Path) -> bool {
    std::fs::metadata(p).map(|m| m.is_file()).unwrap_or(false)
}

Prevention

When it happens

Trigger: open_state_lock runs on a non-Unix/non-Windows platform (e.g. wasm or another target) and the lock file at the given path is not a regular file.

Common situations: Running on an unusual target (embedded, wasm, custom platform) where the lock path was pre-created as a directory or special node, or stale/corrupted state left by another tool.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/92168e7cbbbf9042. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/skill_state.rs:238

#[cfg(windows)]
fn validate_state_lock(path: &Path, file: &fs::File) -> Result<()> {
    use std::os::windows::fs::MetadataExt as _;

    const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;
    let metadata = file
        .metadata()
        .with_context(|| format!("inspect skill state lock at {}", path.display()))?;
    anyhow::ensure!(
        metadata.is_file() && metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0,
        "skill state lock at {} must be a regular, non-reparse file",
        path.display()
    );
    Ok(())
}

#[cfg(all(not(unix), not(windows)))]
fn validate_state_lock(path: &Path, file: &fs::File) -> Result<()> {
    anyhow::ensure!(
        file.metadata()
            .with_context(|| format!("inspect skill state lock at {}", path.display()))?
            .is_file(),
        "skill state lock at {} must be a regular file",
        path.display()
    );
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;

    fn fresh() -> (TempDir, SkillStateStore) {
        let dir = TempDir::new().unwrap();
        let path = dir.path().join(STATE_FILE_NAME);
        let store = SkillStateStore::load_from(path).unwrap();

View on GitHub (pinned to 433685b202)