sinelaw/fresh · error
Failed to read plugin
Error message
Failed to read plugin {}: {} What it means
File-read failure while loading a plugin module: std::fs::read_to_string on the plugin path failed in load_module_with_source (file missing, permission denied, path is a directory, or the file is not valid UTF-8). The wrapped io::Error is returned so the plugin host can report which plugin failed and why; no code from that plugin is ever evaluated.
Solutions
- Verify the plugin path exists and is spelled correctly (use an absolute path)
- Check file read permissions for the process user
- Ensure the plugin file is valid UTF-8 text
- Re-install/re-sync the plugin if it was deleted or moved
Example fix
// before
backend.load_module("./plugn.ts").await?;
// after
assert!(std::path::Path::new("./plugin.ts").is_file());
backend.load_module("./plugin.ts").await?; Defensive patterns
Strategy: validation
Validate before calling
fn plugin_readable(path: &str) -> Result<(), String> {
let p = std::path::Path::new(path);
if !p.is_file() { return Err(format!("{path}: not a file")); }
std::fs::File::open(p).map_err(|e| format!("{path}: {e}"))?.metadata()?;
Ok(())
} Try / catch
match backend.load_module(path).await {
Err(e) if e.to_string().starts_with("Failed to read plugin") => {
eprintln!("plugin file problem: {e:#}");
resync_plugin_dir();
}
other => other,
} Prevention
- Always pass absolute, verified plugin paths
- Check file existence and permissions at config load time
- Ensure plugin files are UTF-8 text
- Re-sync plugin directories after deploys or updates
When it happens
Trigger: Plugin path does not exist, is a directory, or the process lacks read permission; invalid UTF-8 in the plugin file also fails read_to_string.
Common situations: Typoed or relative plugin path from the wrong working directory; plugin file deleted/moved after registration; permission changes on the plugin directory; non-UTF8 plugin files.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- Failed to read
- Invalid path encoding
- home directory not found
- Filesystem not available
- read_range: expected
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/be17cf7f0732264a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-plugin-runtime/src/backend/quickjs_backend.rs:9051
install_console(&ctx, &globals)?;
ctx.eval::<(), _>(EDITOR_PROMISE_BOOTSTRAP.as_bytes())?;
Ok::<_, rquickjs::Error>(())
})
.map_err(|e| anyhow!("Failed to set up global API: {}", e))?;
Ok(())
}
/// Load and execute a TypeScript/JavaScript plugin from a file path
pub async fn load_module_with_source(
&mut self,
path: &str,
_plugin_source: &str,
) -> Result<()> {
let path_buf = PathBuf::from(path);
let source = std::fs::read_to_string(&path_buf)
.map_err(|e| anyhow!("Failed to read plugin {}: {}", path, e))?;
let filename = path_buf
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("plugin.ts");
// Check for ES imports - these need bundling to resolve dependencies
if has_es_imports(&source) {
// Try to bundle (this also strips imports and exports)
match bundle_module(&path_buf) {
Ok(bundled) => {
self.execute_js(&bundled, path)?;
}
Err(e) => {
tracing::warn!(
"Plugin {} uses ES imports but bundling failed: {}. Skipping.",
path,
eView on GitHub (pinned to 67894ca546)