pydantic/monty · warning · MountError
File name too long
Error message
File name too long
What it means
`MountTable::handle_os_call` rejects OS calls whose primary virtual path exceeds the filesystem's maximum name/path length with a 'File name too long' error (ENAMETOOLONG), matching CPython's OSError for the same condition. The length check runs first and is O(1) so hostile paths cannot make the null-byte scan do unbounded work. Unlike some rejections, this one surfaces as an error rather than a `False` predicate, because CPython raises `OSError` for overlong paths.
Source
Thrown at crates/monty-fs/src/mount_table.rs:113
}
/// Handles an OS call using the mount table.
///
/// Consumes the call so a covered write's payload is *moved* into the
/// backend (overlay storage retains it without a copy). Routing happens
/// on a borrow first, so [`MountCallOutcome::NotHandled`] hands the call
/// back untouched for the caller's fallback handler (a host callback or
/// [`OsFunctionCall::on_no_handler`]).
///
/// Path length and null bytes are checked before anything else touches the
/// path, so both apply whether or not a mount covers it — as in CPython,
/// where neither reaches a syscall.
pub fn handle_os_call(&mut self, call: OsFunctionCall) -> MountCallOutcome {
if let Some(primary_path) = call.fs_primary_path() {
// Length first: it is the only check that stays O(1) on a hostile
// path, so a null scan must not run ahead of it. A path that is
// both reports its length, where CPython reports the null byte.
let rejection = reject_overlong_path(primary_path).err().or_else(|| {
contains_null_byte(primary_path)
.then(|| MountError::EmbeddedNullByte(call.embedded_null_message(false)))
});
if let Some(e) = rejection {
// Both make CPython's predicates answer `False` rather than
// raise — `pathlib` swallows `OSError` and `ValueError` alike.
MountCallOutcome::Handled(if call.is_existence_check() {
Ok(MontyObject::Bool(false))
} else {
Err(e)
})
} else {
match self.route_call(primary_path, &call) {
Some(Ok(index)) => MountCallOutcome::Handled(self.mounts[index].execute(call)),
Some(Err(err)) => MountCallOutcome::Handled(Err(err)),
None => MountCallOutcome::NotHandled(call),
}
}View on GitHub (pinned to adc986b362)
Solutions
- Shorten the file or directory names your sandboxed code uses (keep components under ~255 bytes and total paths well under 4096).
- Truncate or hash long logical names before writing (e.g. store a short digest as the filename and keep the long name in content/metadata).
- Restructure deep output trees into shallower directories to reduce total path length.
Example fix
// before
name = 'log-' + '-'.join(entries) # hundreds of chars
open(name, 'w')
// after
import hashlib
name = 'log-' + hashlib.sha256('-'.join(entries).encode()).hexdigest()[:32]
open(name, 'w') Defensive patterns
Strategy: validation
Validate before calling
import os
def path_fits(path: str, max_total: int = 4096, max_component: int = 255) -> bool:
return len(path.encode()) <= max_total and all(
len(part.encode()) <= max_component for part in path.split('/')
)
if not path_fits(my_virtual_path):
raise ValueError('path too long for mounted filesystem') Type guard
def is_safe_mounted_path(path: str) -> bool:
return (
'\x00' not in path
and len(path.encode()) <= 4096
and all(len(p.encode()) <= 255 for p in path.split('/'))
) Try / catch
try:
result = session.feed_run(f"open({virtual_path!r})")
except MontyRuntimeError as exc:
if 'File name too long' in str(exc):
# shorten/hash the filename and retry
...
raise Prevention
- Hash or truncate generated filenames instead of concatenating long identifiers.
- Keep output directory trees shallow; compute the full virtual path length before writing.
- Sanitize untrusted input used as filenames (length cap, no null bytes).
- When renaming, ensure both source and destination are inside the mount and within length limits.
When it happens
Trigger: Calling any file operation (open, rename, stat, list, mkdir, etc.) through a mount whose virtual path component or full path exceeds the host maximum (commonly NAME_MAX=255 bytes per component or PATH_MAX=4096 total) — e.g. `open('a'*300)` inside the sandbox, or `rename` with an extremely long destination.
Common situations: Generated filenames from untrusted input (hashes concatenated, templated names), deep directory trees pushing total path length past PATH_MAX, or rename operations with one side of the path outside the mount being validated first.
Related errors
- monty binary not found at binaryPath: ${explicit}
- the wasm worker does not support filesystem mounts (browser
- MontyFileHandle path must be a string
- MontyFileHandle mode must be a string
- non-filesystem OS function reached filesystem parser
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/2431038b01e56615.
Report an issue: GitHub.