sinelaw/fresh · error

Invalid path encoding

Error message

Invalid path encoding

What it means

This error is raised when a prepared TypeScript plugin's file path cannot be converted from an OsStr to a valid UTF-8 Rust string via `Path::to_str()`. The runtime needs the path as a `&str` to pass into `execute_js` (typically for `import()` resolution / stack traces inside QuickJS), and non-UTF-8 paths would be silently mangled, so the code fails fast instead. It is a fail-fast guard against OS paths containing invalid Unicode.

Solutions

  1. Rename the plugin file and its parent directories to use only valid UTF-8 (ASCII) characters.
  2. Check the path with `locale`/`ls` on the host to find the offending filename bytes and fix the mount or filesystem encoding.
  3. If programmatic control is needed, sanitize paths before handing them to the plugin loader, or re-encode with `String::from_utf8_lossy` only if the resulting path is still resolvable.
  4. On Windows, ensure the path does not contain characters outside UTF-16-to-UTF-8 representable ranges; move plugins to a simple ASCII path.

Example fix

// before
plugins-dir/caf�-plugins/my-plugin.ts   (raw non-UTF-8 byte in dir name)

// after
plugins-dir/cafe-plugins/my-plugin.ts
Defensive patterns

Strategy: validation

Validate before calling

fn is_utf8_path(p: &std::path::Path) -> bool {
    p.to_str().is_some()
}
// call before loading:
if !is_utf8_path(&prepared.path) {
    eprintln!("skip plugin: path is not valid UTF-8: {:?}", prepared.path);
}

Type guard

fn valid_utf8_path(p: &std::path::Path) -> Option<&str> {
    p.to_str()
}

Try / catch

match load_result {
    Err(e) if e.to_string().contains("Invalid path encoding") => {
        eprintln!("Plugin skipped: file path is not valid UTF-8; rename the file/directory.");
    }
    Err(e) => return Err(e),
    Ok(v) => v,
}

Prevention

When it happens

Trigger: Calling the plugin-loading flow (which prepares a plugin and calls `execute_js(&prepared.js_code, path_str)`) with a plugin file whose absolute path contains bytes that are not valid UTF-8. This happens right after 'Loaded i18n strings for plugin ...' during plugin load.

Common situations: Plugin directories or filenames created with non-UTF-8 encodings (e.g. Latin-1 filenames from old Samba/NFS mounts, filenames with raw 0x80-0xFF bytes on Linux); plugins installed in directories whose names contain such bytes; moving plugin caches across systems with different filename encodings.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

/// must run on the plugin thread.
fn execute_prepared_plugin(
    runtime: &Rc<RefCell<QuickJsBackend>>,
    plugins: &mut HashMap<String, TsPluginInfo>,
    prepared: &PreparedPlugin,
) -> Result<()> {
    // Register i18n strings
    if let Some(ref i18n) = prepared.i18n {
        runtime
            .borrow_mut()
            .services
            .register_plugin_strings(&prepared.name, i18n.clone());
        tracing::debug!("Loaded i18n strings for plugin '{}'", prepared.name);
    }

    let path_str = prepared
        .path
        .to_str()
        .ok_or_else(|| anyhow!("Invalid path encoding"))?;

    let exec_start = std::time::Instant::now();
    runtime
        .borrow_mut()
        .execute_js(&prepared.js_code, path_str)?;
    let exec_elapsed = exec_start.elapsed();

    tracing::debug!(
        "execute_prepared_plugin: plugin '{}' executed in {:?}",
        prepared.name,
        exec_elapsed
    );

    plugins.insert(
        prepared.name.clone(),
        TsPluginInfo {
            name: prepared.name.clone(),
            path: prepared.path.clone(),

View on GitHub (pinned to 67894ca546)