ruvnet/RuView · error · anyhow::Error

failed to create data_dir {}: {e}

Error message

failed to create data_dir {}: {e}

What it means

`TrainingSession::new` runs `std::fs::create_dir_all` on the sanitized data dir. Any mkdir failure is reported with the target path: a path component already exists as a regular file, EACCES when the user cannot write an ancestor, or EROFS on a read-only filesystem. Sanitization has already passed at this point, so the cause is environmental, not traversal.

Source

Thrown at v2/crates/wifi-densepose-pointcloud/src/training.rs:167

            near_clip: 0.3,
            far_clip: 8.0,
            gamma: 1.0,
            samples_used: 0,
            rmse: f32::MAX,
        }
    }
}

impl TrainingSession {
    /// Create a new training session rooted at `data_dir`.
    ///
    /// `data_dir` must not contain `..` components — we reject path traversal
    /// attempts from CLI/API input. The directory is created if missing and
    /// then canonicalised so every subsequent write stays inside it.
    pub fn new(data_dir: &str) -> Result<Self> {
        let path = sanitize_data_path(data_dir)?;
        std::fs::create_dir_all(&path)
            .map_err(|e| anyhow!("failed to create data_dir {}: {e}", path.display()))?;
        // Canonicalise so path-traversal checks in safe_join have a fixed root.
        let path = path
            .canonicalize()
            .map_err(|e| anyhow!("cannot canonicalise data_dir {}: {e}", path.display()))?;

        // Load existing calibration if available
        let cal_path = safe_join(&path, "calibration.json")
            // safe_join needs the parent to exist; for initial load that's always data_dir
            .or_else(|_| Ok::<_, anyhow::Error>(path.join("calibration.json")))?;
        let calibration = if cal_path.exists() {
            let data = std::fs::read_to_string(&cal_path)?;
            serde_json::from_str(&data).unwrap_or_default()
        } else {
            DepthCalibration::default()
        };

        Ok(Self {
            samples: Vec::new(),

View on GitHub (pinned to 4685618388)

Solutions

  1. Remove or rename the regular file blocking the path (`rm runs`) and retry.
  2. Choose a writable location (e.g. under `$HOME` or a mounted volume) or grant write permission on the ancestor / remount rw.
  3. Diagnose per-component with `namei -l <path>` to find where access stops.

Example fix

# before
ruview-train --data-dir /mnt/usb/runs     # read-only mount -> failed to create data_dir
# after
mount -o remount,rw /mnt/usb || ruview-train --data-dir /home/op/runs
Defensive patterns

Strategy: validation

Validate before calling

fn data_dir_creatable(raw: &str) -> bool {
    let p = std::path::Path::new(raw);
    // no component may already exist as a non-directory
    let mut cur = std::path::PathBuf::new();
    for c in p.components() {
        cur.push(c);
        if cur.exists() && !cur.is_dir() {
            return false;
        }
    }
    true
}

Prevention

When it happens

Trigger: `runs.json` exists as a file and `runs.json/sub` is requested; unprivileged user targeting `/var/lib/ruview`; a read-only rootfs or ro-mounted USB stick at the target; SELinux denying dir creation.

Common situations: Embedded or container images with read-only rootfs and no writable volume; stale files occupying the intended directory name; wrong HOME when running under a service manager.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/95131776bdde668b. Report an issue: GitHub.