{"record":{"id":"ab9f88d415662750","repo":"astrid-runtime/astrid","slug":"winfsp-lease-paths-are-malformed","errorCode":null,"errorMessage":"WinFsp lease paths are malformed","messagePattern":"WinFsp lease paths are malformed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-winfsp/src/win.rs","lineNumber":272,"sourceCode":"    if launch.parent.start_identity.is_none() {\n        bail!(\"WinFsp service parent start identity is required on Windows\");\n    }\n    let lease = &launch.lease;\n    let now = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .context(\"read system clock\")?\n        .as_secs();\n    if lease.expires_at_epoch_secs < now {\n        bail!(\"WinFsp lease is expired\");\n    }\n    if lease.lease_token.len() < 16 || lease.lease_token.len() > 4096 {\n        bail!(\"WinFsp lease callback token is invalid\");\n    }\n    if !lease.resource_path.is_absolute()\n        || !lease.callback_path.is_absolute()\n        || lease.callback_path != lease.resource_path.join(\"control.endpoint\")\n    {\n        bail!(\"WinFsp lease paths are malformed\");\n    }\n    platform_fs::validate_private_directory(&lease.resource_path)\n        .context(\"validate private WinFsp lease resource\")?;\n    platform_fs::verify_no_redirects(&lease.resource_path)\n        .context(\"reject redirected WinFsp lease resource\")?;\n    let manifest_path = lease.resource_path.join(\"lease.json\");\n    platform_fs::validate_private_file(&manifest_path)\n        .context(\"validate private WinFsp lease manifest\")?;\n    let manifest = std::fs::read(&manifest_path).context(\"read WinFsp lease manifest\")?;\n    if manifest.len() > 64 * 1024 {\n        bail!(\"WinFsp lease manifest exceeds the bounded size\");\n    }\n    let admitted: StorageMountLeaseV1 =\n        serde_json::from_slice(&manifest).context(\"decode WinFsp lease manifest\")?;\n    if admitted != *lease {\n        bail!(\"WinFsp launch lease does not match the kernel manifest\");\n    }\n    if !launch.mountpoint.is_absolute()","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-winfsp/src/win.rs#L254-L290","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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\")."],"exampleFix":"// before\nlease.callback_path = PathBuf::from(\"control.endpoint\");\n// after\nlease.callback_path = lease.resource_path.join(\"control.endpoint\");","handlingStrategy":"validation","validationCode":"let rp = dunce::canonicalize(&lease.resource_path)?;\nlet cp = dunce::canonicalize(&lease.callback_path)?;\nif !rp.is_absolute() || !cp.is_absolute() || cp != rp.join(\"control.endpoint\") {\n    return Err(anyhow!(\"lease paths malformed: callback_path must equal resource_path/control.endpoint\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["winfsp","paths","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}