sinelaw/fresh · error

Invalid plugin filename

Error message

Invalid plugin filename

What it means

prepare_plugin rejects a plugin path whose final path component has no valid UTF-8 file stem (path.file_stem() returns None or the stem is not valid UTF-8). This fires during plugin preparation when the discovered plugin file has a non-UTF-8 or missing stem (e.g. an odd raw-bytes filename); preparation aborts instead of producing a PreparedPlugin.

Solutions

  1. Rename the plugin file so its stem is valid UTF-8 plain ASCII (e.g. my-plugin.ts).
  2. Remove or fix the offending file in the plugins directory and rescan/reload plugins.
  3. If the file came from an archive or copy tool, re-extract/copy with a UTF-8-capable locale so the filename round-trips correctly.
  4. Check the plugin discovery path configuration for stray malformed filenames.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/fresh-plugin-runtime/src/thread.rs:1429 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/a3bc8615f2a84c12. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-plugin-runtime/src/thread.rs:1429

    /// `.d.ts` emit for the plugin source, produced by oxc's
    /// isolated-declarations transformer. Present on every successful
    /// TS/JS prepare; callers can use it to assemble a consolidated
    /// plugins.d.ts so init.ts/other plugins can reach each plugin's
    /// public types without manual `as`-casts. `None` only when
    /// isolated-declarations emit failed outright — the plugin still
    /// loads at runtime.
    declarations: Option<String>,
}

/// Prepare a plugin for execution: read source, transpile, extract dependencies.
///
/// This function does I/O and CPU-bound work only — no QuickJS interaction.
/// It is safe to call from any thread (all inputs/outputs are Send).
fn prepare_plugin(path: &Path) -> Result<PreparedPlugin> {
    let plugin_name = path
        .file_stem()
        .and_then(|s| s.to_str())
        .ok_or_else(|| anyhow!("Invalid plugin filename"))?
        .to_string();

    let source = std::fs::read_to_string(path)
        .map_err(|e| anyhow!("Failed to read plugin {}: {}", path.display(), e))?;

    let filename = path
        .file_name()
        .and_then(|s| s.to_str())
        .unwrap_or("plugin.ts");

    // Extract dependencies before transpilation
    let dependencies = fresh_parser_js::extract_plugin_dependencies(&source);

    // Emit `.d.ts` via oxc's isolated-declarations before the
    // transpile step consumes `source`. We want the raw TS (every
    // `export type`, `export interface`, and `declare global` block
    // the plugin author wrote) so downstream plugins and init.ts
    // reach the plugin's public types without casts. Failures are

View on GitHub (pinned to 67894ca546)