astrid-runtime/astrid · error
WinFsp lease paths are malformed
Error message
WinFsp lease paths are malformed
What it means
The lease declares a resource_path (private state directory) and a callback_path for its control endpoint. The validator requires both to be absolute and requires callback_path to be exactly resource_path joined with "control.endpoint". Any deviation — relative paths, a moved or renamed callback path, or a resource/callback mismatch — is rejected as malformed before the mount starts.
Solutions
- Regenerate the launch descriptor so callback_path is computed as resource_path.join("control.endpoint") rather than set independently.
- Canonicalize both paths (dunce::canonicalize or fs::canonicalize) before storing them so prefixes and separators match exactly.
- Fix launchers that relocate the resource directory to rewrite callback_path (and control_path) at the same time.
- Validate at descriptor-build time: assert resource_path.is_absolute() && callback_path == resource_path.join("control.endpoint").
Example fix
// before
lease.callback_path = PathBuf::from("control.endpoint");
// after
lease.callback_path = lease.resource_path.join("control.endpoint"); Defensive patterns
Strategy: validation
Validate before calling
let rp = dunce::canonicalize(&lease.resource_path)?;
let cp = dunce::canonicalize(&lease.callback_path)?;
if !rp.is_absolute() || !cp.is_absolute() || cp != rp.join("control.endpoint") {
return Err(anyhow!("lease paths malformed: callback_path must equal resource_path/control.endpoint"));
} Prevention
- Always derive callback_path via resource_path.join("control.endpoint") — never set it independently
- Canonicalize paths before persisting them in descriptors
- When relocating a resource directory, rewrite all dependent paths in one place
- Unit-test the descriptor builder against relative inputs
When it happens
Trigger: service_main -> validate_service_launch when lease.resource_path is relative, lease.callback_path is relative, or callback_path != resource_path.join("control.endpoint") (e.g. custom endpoint name, nested subdir, or trailing separators/case differences causing inequality).
Common situations: A launcher rewrites the resource directory (relocation, different drive letter) but leaves callback_path stale; templates that use a custom endpoint filename; building paths from relative working-directory strings instead of canonical absolute paths; path normalization differences (forward slashes, \\?\ prefixes) making semantically equal paths compare unequal.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- WinFsp daemon lease contains a relative endpoint
- FSKit lease paths must be absolute
- FUSE callback path is not the kernel lease endpoint
- FUSE lease paths must be absolute
- invalid destination parent
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ab9f88d415662750.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-storage-provider-winfsp/src/win.rs:272
if launch.parent.start_identity.is_none() {
bail!("WinFsp service parent start identity is required on Windows");
}
let lease = &launch.lease;
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.context("read system clock")?
.as_secs();
if lease.expires_at_epoch_secs < now {
bail!("WinFsp lease is expired");
}
if lease.lease_token.len() < 16 || lease.lease_token.len() > 4096 {
bail!("WinFsp lease callback token is invalid");
}
if !lease.resource_path.is_absolute()
|| !lease.callback_path.is_absolute()
|| lease.callback_path != lease.resource_path.join("control.endpoint")
{
bail!("WinFsp lease paths are malformed");
}
platform_fs::validate_private_directory(&lease.resource_path)
.context("validate private WinFsp lease resource")?;
platform_fs::verify_no_redirects(&lease.resource_path)
.context("reject redirected WinFsp lease resource")?;
let manifest_path = lease.resource_path.join("lease.json");
platform_fs::validate_private_file(&manifest_path)
.context("validate private WinFsp lease manifest")?;
let manifest = std::fs::read(&manifest_path).context("read WinFsp lease manifest")?;
if manifest.len() > 64 * 1024 {
bail!("WinFsp lease manifest exceeds the bounded size");
}
let admitted: StorageMountLeaseV1 =
serde_json::from_slice(&manifest).context("decode WinFsp lease manifest")?;
if admitted != *lease {
bail!("WinFsp launch lease does not match the kernel manifest");
}
if !launch.mountpoint.is_absolute()View on GitHub (pinned to affd8760f4)