ruvnet/RuView · error · anyhow::Error

cannot canonicalise data_dir {}: {e}

Error message

cannot canonicalise data_dir {}: {e}

What it means

Immediately after creating it, `TrainingSession::new` canonicalizes the data dir so `safe_join` has a fixed containment root. Failure here means the directory could not be resolved after creation: it vanished between `create_dir_all` and `canonicalize`, a parent denies traversal, or the path traverses a symlink loop / flaky FUSE-automount. It is rare and usually indicates a race or an exotic filesystem rather than a config mistake.

Source

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

            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(),
            calibration,
            data_dir: path,
        })
    }

View on GitHub (pinned to 4685618388)

Solutions

  1. Retry session creation once the interfering cleanup stops (typically succeeds on the second attempt).
  2. Pass a stable, symlink-free absolute path for the data dir.
  3. Keep the data dir on a local, always-mounted filesystem during capture/training runs.

Example fix

// before
let s = TrainingSession::new(&dir)?; // cleaner deletes dir between mkdir and canonicalize
// after
let s = TrainingSession::new(&dir)
    .or_else(|_| TrainingSession::new(&dir))?; // retry once after the race window
Defensive patterns

Strategy: retry

Validate before calling

fn data_dir_resolvable(dir: &std::path::Path) -> bool {
    dir.is_dir() && dir.canonicalize().is_ok()
}

Try / catch

let session = match TrainingSession::new(&dir) {
    Ok(s) => s,
    Err(e) if e.to_string().contains("cannot canonicalise data_dir") => {
        // transient race or flaky mount: recreate once, then retry
        std::fs::create_dir_all(&dir)?;
        TrainingSession::new(&dir)?
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: A concurrent cleaner (`rm -rf` in a test harness) deleting the fresh dir; cyclic symlinks in the path; transient FUSE/automount errors; NFS stale file handles immediately after creation.

Common situations: Test suites that wipe output dirs while sessions start; overlayfs quirks inside containers; NAS-backed data roots with aggressive automount timeouts.

Related errors


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