zeroclaw-labs/zeroclaw · error · anyhow::Error

cannot resolve SOP path '{}'

Error message

cannot resolve SOP path '{}'

What it means

resolve_existing_ancestor walks the path upward via file_name()/parent(); when a path has no file_name (empty path '' or filesystem root '/') or no parent, the (Some(name), Some(parent)) match arm fails and the walk cannot continue. This error means the path being resolved is degenerate - there is no component left to resolve. It surfaces through ensure_within_root during apply_proposal path checks.

Source

Thrown at crates/zeroclaw-runtime/src/sop/procedural_memory.rs:436

                .with_context(|| format!("canonicalize '{}'", current.display()))?;
            for name in remainder.iter().rev() {
                resolved.push(name);
            }
            return Ok(resolved);
        }
        match (current.file_name(), current.parent()) {
            (Some(name), Some(parent)) => {
                let component = Path::new(name);
                if component
                    .components()
                    .any(|c| !matches!(c, Component::Normal(_)))
                {
                    bail!("unsafe SOP path component '{}'", name.to_string_lossy());
                }
                remainder.push(name);
                current = parent;
            }
            _ => bail!("cannot resolve SOP path '{}'", path.display()),
        }
    }
}

fn ensure_relative_component(component: &str) -> Result<()> {
    let path = Path::new(component);
    if path
        .components()
        .any(|c| !matches!(c, Component::Normal(_)))
    {
        bail!("unsafe SOP path component");
    }
    Ok(())
}

fn hash_sop_dir(dir: &Path) -> Result<String> {
    let mut hasher = Sha256::new();
    for name in ["SOP.toml", "SOP.md"] {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Set sops_dir to a concrete subdirectory (never '' or '/'), e.g. '/var/lib/zeroclaw/sops'
  2. Verify install_root is a real directory path, not the filesystem root
  3. Guard callers: reject empty or '/' install roots before invoking apply_proposal

Example fix

# before (config)
sops_dir = "/"

# after
sops_dir = "sops"   # resolved under install_root, non-root
Defensive patterns

Strategy: validation

Validate before calling

fn resolvable_path(p: &Path) -> bool {
    !p.as_os_str().is_empty() && p.file_name().is_some() && p.parent().is_some()
}

Try / catch

Err(e) if e.to_string().contains("cannot resolve SOP path") => {
    // log the offending path from the message; fix the config entry that produced it
}

Prevention

When it happens

Trigger: apply_proposal path containment checks receiving an empty path or '/' as sops_root or target - e.g. sops_dir misconfigured to '/' or '', making resolve_sops_dir produce a root-level path with no name/parent components.

Common situations: An empty or root sops_dir setting in config, an install_root of '/' in containerized deployments, or string manipulation upstream producing an empty path string.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/7556841b7faaedcd. Report an issue: GitHub.