Y2Z/monolith · critical
unable to read file
Error message
unable to read file
What it means
In retrieve_asset, when a file:// URL resolves to an existing regular file, the code calls fs::read(path).expect("unable to read file"). If the read fails despite the file existing (permission denied, it is a special file like a FIFO/device, it was deleted between the exists() check and the read, or I/O error), the expect panics, unwinding the process rather than returning the reqwest::Error the function signature promises.
Source
Thrown at src/session.rs:98
self.client.get("").send()?;
}
let path_buf: PathBuf = url.to_file_path().unwrap().clone();
let path: &Path = path_buf.as_path();
if path.exists() {
if path.is_dir() {
if !self.options.silent {
print_error_message(&format!("{} (is a directory)", &cache_key));
}
// Provoke error
Err(self.client.get("").send().unwrap_err())
} else {
if !self.options.silent {
print_info_message(&cache_key.to_string());
}
let file_blob: Vec<u8> = fs::read(path).expect("unable to read file");
Ok((
file_blob.clone(),
url.clone(),
detect_media_type(&file_blob, url),
"".to_string(),
))
}
} else {
if !self.options.silent {
print_error_message(&format!("{} (file not found)", &url));
}
// Provoke error
Err(self.client.get("").send().unwrap_err())
}
} else if self.cache.is_some() && self.cache.as_ref().unwrap().contains_key(&cache_key) {
// URL is in cache, we get and return itView on GitHub (pinned to a6fc8d0095)
Solutions
- Check that the process user has read permission on the target path (ls -l, or run as a user with access) before invoking the tool
- Verify the file:// URL points to a regular readable file, not a device/fifo or dangling symlink
- Pre-check with std::fs::metadata(path).map(|m| m.is_file()) and File::open in calling code to fail gracefully before retrieve_asset
- Patch the code to replace expect with match fs::read(path) and map the io::Error into the function's Err type
Example fix
// before
let file_blob: Vec<u8> = fs::read(path).expect("unable to read file");
// after
let file_blob: Vec<u8> = fs::read(path).map_err(|e| {
eprintln!("unable to read file {}: {}", path.display(), e);
self.client.get("").send().unwrap_err()
})?; Defensive patterns
Strategy: validation
Validate before calling
fn is_readable_file(url: &url::Url) -> bool {
url.to_file_path().ok()
.and_then(|p| std::fs::metadata(p).ok())
.map(|m| m.is_file())
.unwrap_or(false)
&& url.to_file_path().ok()
.map(|p| std::fs::File::open(p).is_ok())
.unwrap_or(false)
} Type guard
fn is_readable_regular_file(path: &std::path::Path) -> bool {
std::fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)
&& std::fs::File::open(path).is_ok()
} Try / catch
// retrieve_asset returns Result; catch the provoked reqwest::Error and check fs state
match session.retrieve_asset(&parent, &file_url) {
Ok((blob, _, media_type, _)) => { /* use blob */ }
Err(e) => {
let path = file_url.to_file_path().unwrap();
if !path.exists() { eprintln!("file not found: {}", path.display()); }
else if std::fs::File::open(&path).is_err() { eprintln!("permission denied: {}", path.display()); }
else { eprintln!("asset retrieval failed: {}", e); }
}
} Prevention
- Pre-open files with File::open before passing file:// URLs
- Run the tool with a user that has read access to all referenced local assets
- Avoid file:// URLs pointing at special files, fifos, or dangling symlinks
- Don't delete or move local assets while a monolithic build is in progress
When it happens
Trigger: Calling retrieve_asset (directly or via create_monolithic_document / read_local_file_with_* helpers) with a file:// URL whose path exists and is not a directory but cannot be opened for reading: permission-denied file, unreadable special file (e.g. /dev/... mapped via file://), or a race where the file disappears between path.exists() and fs::read.
Common situations: Running monolith as a different user than the file owner (e.g. in CI or a container with restricted permissions); pointing at files under a mounted volume with no read bit; a symlink pointing to a deleted target; TOCTOU deletion on a tmp file.
Related errors
- could not prepare output
- could not write output
- Time went backwards
- unable to serialize DOM into buffer
- Unable to serialize DOM into buffer
AI-assisted analysis of Y2Z/monolith@a6fc8d0095 (2026-09-05).
Data as JSON: /api/errors/013f1652ccfdfd43.
Report an issue: GitHub.