ruvnet/RuView · error

cannot create output dir {}: {e}

Error message

cannot create output dir {}: {e}

What it means

std::fs::create_dir_all failed for the calibrate-serve --output-dir at HTTP API startup. It is an OS-level io::Error: permission denied on a path component, a component that already exists as a regular file, an invalid path string, or an I/O error on the volume. The server aborts before binding any socket.

Source

Thrown at v2/crates/wifi-densepose-cli/src/calibrate_api.rs:315

        .route("/api/v1/calibration/health", get(health))
        .route("/api/v1/calibration/start", post(start))
        .route("/api/v1/calibration/status", get(status_handler))
        .route("/api/v1/calibration/stop", post(stop))
        .route("/api/v1/calibration/result", get(result))
        .route("/api/v1/calibration/baselines", get(baselines))
        .route("/api/v1/room/state", get(room_state))
        .route("/api/v1/room/train", post(train_room))
        .route("/api/v1/enroll/anchor", post(enroll_anchor))
        .route("/api/v1/enroll/geometry", post(enroll_geometry))
        .route("/api/v1/enroll/status", get(enroll_status))
        .layer(CorsLayer::permissive())
        .with_state(state)
}

/// Run the calibration HTTP API server (blocks until Ctrl-C).
pub async fn execute(args: CalibrateServeArgs) -> Result<()> {
    std::fs::create_dir_all(&args.output_dir)
        .map_err(|e| anyhow::anyhow!("cannot create output dir {}: {e}", args.output_dir))?;

    let udp_addr = format!("{}:{}", args.udp_bind, args.udp_port);
    let socket = UdpSocket::bind(&udp_addr)
        .await
        .map_err(|e| anyhow::anyhow!("cannot bind UDP socket on {udp_addr}: {e}"))?;
    eprintln!("[calibrate-serve] CSI ingest on udp://{udp_addr}");

    let status = Arc::new(RwLock::new(SharedStatus {
        udp_port: args.udp_port,
        default_tier: args.tier.clone(),
        output_dir: args.output_dir.clone(),
        ..Default::default()
    }));

    let (cmd_tx, cmd_rx) = mpsc::channel::<CalCommand>(8);
    let window = Arc::new(RwLock::new(VecDeque::<f32>::with_capacity(LIVE_WINDOW)));
    let enroll = Arc::new(RwLock::new(HashMap::<String, RoomEnroll>::new()));

View on GitHub (pinned to 4685618388)

Solutions

  1. Manually run mkdir -p as the same user the server runs as to surface the exact OS error
  2. Remove or rename the regular file occupying the path
  3. Point --output-dir to a writable volume or mount one at that path
  4. Fix ownership/permissions on the parent directory (chown/chmod)

Example fix

// before
$ ruview calibrate-serve --output-dir /var/lib/ruview/baselines   # create_dir_all fails

// after
$ mkdir -p /var/lib/ruview/baselines && chown $(id -u) /var/lib/ruview/baselines
$ ruview calibrate-serve --output-dir /var/lib/ruview/baselines
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_output_dir(path: &str) -> Result<(), String> {
    let p = std::path::Path::new(path);
    if p.exists() && !p.is_dir() {
        return Err(format!("{path} exists and is not a directory"));
    }
    std::fs::create_dir_all(p).map_err(|e| format!("cannot create {path}: {e}"))
}

Prevention

When it happens

Trigger: Starting calibrate-serve with --output-dir whose parent is not writable; --output-dir naming an existing regular file; a read-only mount (container rootfs); a path containing an invalid component.

Common situations: Container images where the data volume is not mounted at the configured path; CI or services running as non-root against root-owned paths; a previous run leaving a file where the directory is expected.

Related errors


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