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

  1. Verify the plugin path exists and is spelled correctly (use an absolute path)
  2. Check file read permissions for the process user
  3. Ensure the plugin file is valid UTF-8 text
  4. 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

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


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,
                        e

View on GitHub (pinned to 67894ca546)