astrid-runtime/astrid · error · io::Error
InvalidInput
InvalidInput
Error message
private Windows file has no parent directory
What it means
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.
Source
Thrown at crates/astrid-core/src/platform_fs/windows/private_file.rs:20
use super::error::with_context;
use super::executable::{acquire_private_file_transaction_lock, recovery_error};
use super::io::{
FileContract, PreparationCleanup, flush_guarded_open_file, guarded_file_exists,
hash_guarded_regular_file, move_guarded_file, open_guarded_regular_file,
read_guarded_regular_file, remove_guarded_file, replace_file_checked, stage_transaction_copy,
stage_transaction_copy_authenticated, stage_unique_bytes, stage_unique_bytes_retained,
validate_file_contract,
};
use super::path::{
BoundaryContract, TrustedPathGuard, file_identity, validate_local_absolute_path,
};
use super::prelude::*;
pub(in crate::platform_fs) fn read_private_file_to_string(path: &Path) -> io::Result<String> {
validate_local_absolute_path(path)?;
let parent = path.parent().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"private Windows file has no parent directory",
)
})?;
let guard = TrustedPathGuard::capture(parent)?;
guard.verify_contract(BoundaryContract::ExactPrivateDirectory)?;
let _transaction_lock = acquire_private_file_transaction_lock(parent, &guard)?;
recover_private_file_transaction_locked(parent, &guard)?;
guard.verify_contract(BoundaryContract::ExactPrivateDirectory)?;
let mut file = open_guarded_regular_file(&guard, path, FileContract::ExactPrivate)?;
let identity = file_identity(&file)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
validate_file_contract(
file.as_raw_handle().cast(),
path,
FileContract::ExactPrivate,View on GitHub (pinned to affd8760f4)
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.
Example fix
// before
let path = Path::new("C:\\ProgramData\\Astrid").file_name(); // wrong shape
read_private_file_to_string(&dir_only_path)?;
// after
let path = Path::new("C:\\ProgramData\\Astrid\\secret.key");
read_private_file_to_string(path)?; Defensive patterns
Strategy: validation
Validate before calling
fn readable_private(p: &Path) -> bool {
is_local_disk_path(p) && p.parent().is_some() && p.file_name().is_some()
} Try / catch
match read_private_file_to_string(path) {
Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains("no parent directory") => {
// config/path-builder bug: full path with directory required
},
other => other?,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- capsule source is neither a directory nor a regular file: {}
- mountpoint is not a directory: {}
- Astrid volume is not a regular file
- legacy env/secret path is not a regular directory: {}
- Source path does not exist: {source}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/d49bcdb101671b43.
Report an issue: GitHub.