{"record":{"id":"d49bcdb101671b43","repo":"astrid-runtime/astrid","slug":"invalidinput-d49bcd","errorCode":"InvalidInput","errorMessage":"private Windows file has no parent directory","messagePattern":"private Windows file has no parent directory","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/platform_fs/windows/private_file.rs","lineNumber":20,"sourceCode":"\nuse super::error::with_context;\nuse super::executable::{acquire_private_file_transaction_lock, recovery_error};\nuse super::io::{\n    FileContract, PreparationCleanup, flush_guarded_open_file, guarded_file_exists,\n    hash_guarded_regular_file, move_guarded_file, open_guarded_regular_file,\n    read_guarded_regular_file, remove_guarded_file, replace_file_checked, stage_transaction_copy,\n    stage_transaction_copy_authenticated, stage_unique_bytes, stage_unique_bytes_retained,\n    validate_file_contract,\n};\nuse super::path::{\n    BoundaryContract, TrustedPathGuard, file_identity, validate_local_absolute_path,\n};\nuse super::prelude::*;\n\npub(in crate::platform_fs) fn read_private_file_to_string(path: &Path) -> io::Result<String> {\n    validate_local_absolute_path(path)?;\n    let parent = path.parent().ok_or_else(|| {\n        io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"private Windows file has no parent directory\",\n        )\n    })?;\n    let guard = TrustedPathGuard::capture(parent)?;\n    guard.verify_contract(BoundaryContract::ExactPrivateDirectory)?;\n    let _transaction_lock = acquire_private_file_transaction_lock(parent, &guard)?;\n    recover_private_file_transaction_locked(parent, &guard)?;\n    guard.verify_contract(BoundaryContract::ExactPrivateDirectory)?;\n\n    let mut file = open_guarded_regular_file(&guard, path, FileContract::ExactPrivate)?;\n    let identity = file_identity(&file)?;\n    let mut contents = String::new();\n    file.read_to_string(&mut contents)?;\n    validate_file_contract(\n        file.as_raw_handle().cast(),\n        path,\n        FileContract::ExactPrivate,","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/platform_fs/windows/private_file.rs#L2-L38","documentation":"read_private_file_to_string validates the path as a local absolute path and then needs the parent directory to capture and verify a TrustedPathGuard with ExactPrivateDirectory contract. If Path::parent() returns None (a componentless/root path), the directory boundary cannot be established and the read fails with InvalidInput.","triggerScenarios":"Calling read_private_file_to_string with a path whose parent() is None — e.g. a bare root like `C:\\`, a path built from only a prefix component, or a path stripped of its directory portion.","commonSituations":"Accidentally passing the config key or file name alone instead of the full path; a config value that lost its directory during templating; treating a directory path as a file path.","solutions":["Pass the complete absolute file path including its directory (e.g. `C:\\ProgramData\\Astrid\\secret.key`).","Fix the code that builds the path so the directory component is not dropped (avoid Path::file_name-only round trips).","Check path.parent().is_some() before calling, and fail with a clearer application-level error."],"exampleFix":"// before\nlet path = Path::new(\"C:\\\\ProgramData\\\\Astrid\").file_name(); // wrong shape\nread_private_file_to_string(&dir_only_path)?;\n// after\nlet path = Path::new(\"C:\\\\ProgramData\\\\Astrid\\\\secret.key\");\nread_private_file_to_string(path)?;","handlingStrategy":"validation","validationCode":"fn readable_private(p: &Path) -> bool {\n    is_local_disk_path(p) && p.parent().is_some() && p.file_name().is_some()\n}","typeGuard":null,"tryCatchPattern":"match read_private_file_to_string(path) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains(\"no parent directory\") => {\n        // config/path-builder bug: full path with directory required\n    },\n    other => other?,\n}","preventionTips":["Store the full absolute path in config, never a bare file name or key.","Join directory + file name at the last moment and keep both components.","Test config resolution end-to-end so directory components aren't dropped."],"tags":["windows","path","filesystem","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}