astrid-runtime/astrid · error

mountpoint is not valid UTF-16

Error message

mountpoint is not valid UTF-16

What it means

The WinFsp daemon converts the mountpoint OsStr to a U16CString (UTF-16) as required by the WinFsp API. If the path cannot be represented as valid UTF-16, the daemon aborts with this error before starting the filesystem.

Source

Thrown at crates/astrid-storage-provider-winfsp/src/win.rs:81

        || !lease.callback_path.is_absolute()
    {
        bail!("WinFsp daemon lease contains a relative endpoint");
    }

    let runtime = Arc::new(
        tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .context("start WinFsp callback runtime")?,
    );
    let callback = CallbackFs::new(lease.clone(), Arc::clone(&runtime))
        .map_err(|failure| anyhow::anyhow!("build WinFsp callback filesystem: {failure:?}"))?;
    let control_path = provider_control_path(&lease.mount_id)?;
    let control_listener = local_transport::bind(&control_path)
        .with_context(|| format!("bind WinFsp control endpoint {}", control_path.display()))?;
    initialize_winfsp()?;
    let mountpoint = U16CString::from_os_str(start.mountpoint.as_os_str())
        .map_err(|_| anyhow::anyhow!("mountpoint is not valid UTF-16"))?;
    let filesystem = FileSystem::start(volume_params(lease.access), Some(&mountpoint), callback)
        .map_err(|status| {
            anyhow::anyhow!("WinFsp failed to start mount with status {status:#x}")
        })?;
    wait_for_mountpoint_ready(&start.mountpoint)?;

    let mut stdout = std::io::stdout().lock();
    writeln!(stdout, "READY {0}", lease.mount_id).context("report WinFsp readiness")?;
    stdout.flush().context("flush WinFsp readiness")?;

    let result = runtime.block_on(daemon_loop(filesystem, control_listener));
    if let Err(error) = result {
        eprintln!("{PROVIDER_NAME}: daemon stopped after failure: {error:#}");
        return Err(error);
    }
    Ok(())
}

View on GitHub (pinned to affd8760f4)

Solutions

  1. Use a mountpoint path with valid Unicode characters (ASCII drive-letter paths are safest)
  2. Sanitize/validate the mountpoint path before spawning the daemon
  3. Log the raw path bytes to identify the offending component
  4. Avoid constructing paths from raw byte slices; use PathBuf from valid strings

Example fix

// before: opaque failure
let mountpoint = U16CString::from_os_str(start.mountpoint.as_os_str())
    .map_err(|_| anyhow::anyhow!("mountpoint is not valid UTF-16"))?;
// after: include the path in the error
.map_err(|_| anyhow::anyhow!("mountpoint is not valid UTF-16: {}", start.mountpoint.display()))?;
Defensive patterns

Strategy: validation

Validate before calling

// ensure the mountpoint string is valid Unicode before spawning the daemon
let s = start.mountpoint.to_str()
    .ok_or_else(|| "mountpoint path is not valid Unicode")?;
if s.chars().any(|c| (c as u32) >= 0xD800 && (c as u32) <= 0xDFFF) {
    return Err("mountpoint contains surrogate code points");
}

Try / catch

match result {
    Err(e) if e.to_string().contains("not valid UTF-16") => {
        // prompt user for a different mountpoint path
    }
    r => r,
}

Prevention

When it happens

Trigger: Passing a mountpoint path containing invalid Unicode (unpaired surrogates from WTF-8 on Windows) — rare, but possible with paths built from raw bytes or unusual NTFS names.

Common situations: Programmatically constructed paths with invalid bytes; mountpoint sourced from untrusted config or environment containing surrogate code points; non-Unicode 16-bit path components.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/1e3b66b1fcc9f53b. Report an issue: GitHub.