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
- Rename the plugin file so its stem is valid UTF-8 plain ASCII (e.g. my-plugin.ts).
- Remove or fix the offending file in the plugins directory and rescan/reload plugins.
- 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.
- 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 areView on GitHub (pinned to 67894ca546)