astrid-runtime/astrid · warning
InvalidInput
InvalidInput
Error message
Windows file name is too long
What it means
rename_guarded_file performs an NT-level rename (NtSetInformationFile with FILE_RENAME_INFORMATION) and must pack the destination file name as UTF-16 into a u32 length field. This error fires when the UTF-16 name length (UTF-16 code units × 2 bytes) cannot be represented as u32 — i.e. the destination path component is absurdly long. It is a defensive pre-flight check before allocating and filling the rename buffer.
Solutions
- Audit the code that constructs the destination path — a name this long means the path is being built incorrectly (check for repeated join/format loops).
- Validate the destination file name length before calling replace_file_checked/move_guarded_file (keep components under 255 UTF-16 units per Windows limits).
- Reject or sanitize untrusted input that feeds the destination file name before invoking the guarded move/replace APIs.
Example fix
// before: blindly passing a possibly runaway name
move_guarded_file(&guard, &source, &install_dir.join(&user_supplied_name))?;
// after: pre-validate the component
let name = user_supplied_name;
if name.chars().count() == 0 || name.chars().count() > 255 {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid file name"));
}
move_guarded_file(&guard, &source, &install_dir.join(name))?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: validate the destination component before any guarded rename/move
fn valid_component(name: &str) -> io::Result<()> {
let units = name.encode_utf16().count();
if units == 0 || units > 255 {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "file name component out of range"));
}
if name.contains(['/', '\\', ':', '*', '?', '"', '<', '>', '|']) {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "illegal characters in file name"));
}
Ok(())
} Type guard
fn is_valid_windows_component(name: &str) -> bool {
let units = name.encode_utf16().count();
units > 0 && units <= 255 && !name.contains(['/', '\\', ':', '*', '?', '"', '<', '>', '|'])
} Try / catch
// Rust
match replace_file_checked(&guard, &live, &replacement) {
Ok(()) => finish(),
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {
eprintln!("bad destination path for rename: {e}");
// do not retry: fix the path construction
}
Err(e) => return Err(e),
} Prevention
- Keep file name components at or under 255 UTF-16 units.
- Never build destination names with unbounded loops or raw user input.
- Sanitize/whitelist characters in file names derived from external data.
- Unit-test path construction with adversarial long names.
When it happens
Trigger: Raised in rename_guarded_file (via replace_file_checked / move_guarded_file) when `destination_wide.len() * size_of::<u16>()` overflows u32::try_from. Only reachable with a destination name of more than ~2^31 UTF-16 code units — practically only via corrupted path inputs, unbounded string concatenation, or a bug that passes garbage into `destination`.
Common situations: A bug in caller code building destination paths in a loop (e.g. joining the same component repeatedly); deserialized/path-traversal-ish input containing a runaway file name; unit/integration tests passing malformed paths.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/96b82dc0ac0621d5.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-core/src/platform_fs/windows/io.rs:511
guard.verify_contract(boundary_contract)
}
fn rename_guarded_file(
guard: &TrustedPathGuard,
source: &Path,
destination: &Path,
replace: bool,
) -> io::Result<()> {
let source_name = guarded_child_name(guard, source)?;
let destination_name = guarded_child_name(guard, destination)?;
let source = open_guarded_child(guard, source_name, DELETE | FILE_READ_ATTRIBUTES)?;
let destination_wide = destination_name.encode_wide().collect::<Vec<_>>();
let name_bytes = destination_wide
.len()
.checked_mul(size_of::<u16>())
.and_then(|length| u32::try_from(length).ok())
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "Windows file name is too long")
})?;
let buffer_bytes = size_of::<FILE_RENAME_INFORMATION>()
.checked_add(usize::try_from(name_bytes).expect("u32 length fits usize"))
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "rename buffer overflow"))?;
// `Vec<usize>` supplies native pointer alignment and zero-initializes the
// full fixed structure plus the variable-length UTF-16 name bytes required
// by `NtSetInformationFile`.
let mut buffer = vec![0_usize; buffer_bytes.div_ceil(size_of::<usize>())];
let info = buffer.as_mut_ptr().cast::<FILE_RENAME_INFORMATION>();
let information_class = if replace {
FileRenameInformationEx
} else {
FileRenameInformation
};
// SAFETY: the usize buffer is sufficiently aligned and sized for the
// variable-length FILE_RENAME_INFORMATION followed by the UTF-16 component.
unsafe {
if replace {View on GitHub (pinned to affd8760f4)