{"record":{"id":"013f1652ccfdfd43","repo":"Y2Z/monolith","slug":"unable-to-read-file","errorCode":null,"errorMessage":"unable to read file","messagePattern":"unable to read file","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/session.rs","lineNumber":98,"sourceCode":"                self.client.get(\"\").send()?;\n            }\n\n            let path_buf: PathBuf = url.to_file_path().unwrap().clone();\n            let path: &Path = path_buf.as_path();\n            if path.exists() {\n                if path.is_dir() {\n                    if !self.options.silent {\n                        print_error_message(&format!(\"{} (is a directory)\", &cache_key));\n                    }\n\n                    // Provoke error\n                    Err(self.client.get(\"\").send().unwrap_err())\n                } else {\n                    if !self.options.silent {\n                        print_info_message(&cache_key.to_string());\n                    }\n\n                    let file_blob: Vec<u8> = fs::read(path).expect(\"unable to read file\");\n\n                    Ok((\n                        file_blob.clone(),\n                        url.clone(),\n                        detect_media_type(&file_blob, url),\n                        \"\".to_string(),\n                    ))\n                }\n            } else {\n                if !self.options.silent {\n                    print_error_message(&format!(\"{} (file not found)\", &url));\n                }\n\n                // Provoke error\n                Err(self.client.get(\"\").send().unwrap_err())\n            }\n        } else if self.cache.is_some() && self.cache.as_ref().unwrap().contains_key(&cache_key) {\n            // URL is in cache, we get and return it","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/Y2Z/monolith/blob/a6fc8d009514b2ea271dda2539f19a1f479ebfab/src/session.rs#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet file_blob: Vec<u8> = fs::read(path).expect(\"unable to read file\");\n// after\nlet file_blob: Vec<u8> = fs::read(path).map_err(|e| {\n    eprintln!(\"unable to read file {}: {}\", path.display(), e);\n    self.client.get(\"\").send().unwrap_err()\n})?;","handlingStrategy":"validation","validationCode":"fn is_readable_file(url: &url::Url) -> bool {\n    url.to_file_path().ok()\n        .and_then(|p| std::fs::metadata(p).ok())\n        .map(|m| m.is_file())\n        .unwrap_or(false)\n        && url.to_file_path().ok()\n            .map(|p| std::fs::File::open(p).is_ok())\n            .unwrap_or(false)\n}","typeGuard":"fn is_readable_regular_file(path: &std::path::Path) -> bool {\n    std::fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)\n        && std::fs::File::open(path).is_ok()\n}","tryCatchPattern":"// retrieve_asset returns Result; catch the provoked reqwest::Error and check fs state\nmatch session.retrieve_asset(&parent, &file_url) {\n    Ok((blob, _, media_type, _)) => { /* use blob */ }\n    Err(e) => {\n        let path = file_url.to_file_path().unwrap();\n        if !path.exists() { eprintln!(\"file not found: {}\", path.display()); }\n        else if std::fs::File::open(&path).is_err() { eprintln!(\"permission denied: {}\", path.display()); }\n        else { eprintln!(\"asset retrieval failed: {}\", e); }\n    }\n}","preventionTips":["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"],"tags":["rust","panic","filesystem","permissions","io"],"backgroundTag":"file-read-permission-denied","analyzedSha":"a6fc8d009514b2ea271dda2539f19a1f479ebfab","analyzedAt":"2026-09-05T20:13:04.517Z","contentChangedAt":"2026-09-05T20:13:04.517Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}