sinelaw/fresh · error

Failed to read

Error message

Failed to read {}: {}

What it means

While recursively walking a plugin bundle's import graph, `collect_modules` tries to read each imported file from disk; any I/O failure (missing file, permission, invalid UTF-8 via read_to_string) is wrapped with the path and the underlying io::Error. Bundling cannot proceed without the module's contents.

Solutions

  1. Fix or create the file at the path shown in the message.
  2. Correct the import specifier in the importing module to match the actual file name/case.
  3. Vendor or remove imports of external packages (e.g. node_modules-only deps) not present in the bundle root.
  4. Re-save the file as UTF-8 if it contains invalid bytes.

Example fix

// before
import { helper } from "./heplper.ts"; // typo
// after
import { helper } from "./helper.ts";
Defensive patterns

Strategy: try-catch

Validate before calling

// Check entry and its import targets exist before bundling
let p = std::path::Path::new(entry);
if !p.is_file() { bail!("entry file missing: {}", entry); }

Try / catch

match bundle_module(entry) {
    Err(e) if e.to_string().starts_with("Failed to read ") => {
        log::error!("bundle aborted, missing module: {e}");
        disable_plugin(entry);
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `collect_modules` (directly or via bundle_module) on an entry file whose imports point to a path that doesn't exist, is a directory, lacks read permission, or contains non-UTF-8 bytes.

Common situations: Plugin imports a typo'd relative path; module file deleted or moved after the import was written; importing a node_modules package that isn't vendored on disk; file saved with a non-UTF-8 encoding.

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/e498deac6e6839c6. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-parser-js/src/lib.rs:366

    Ok(output)
}

/// Collect all modules in dependency order (dependencies first)
fn collect_modules(
    path: &Path,
    visited: &mut HashSet<PathBuf>,
    modules: &mut Vec<ModuleMetadata>,
    path_to_var: &mut std::collections::HashMap<PathBuf, String>,
) -> Result<()> {
    let canonical = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
    if visited.contains(&canonical) {
        return Ok(()); // Already processed (circular import protection)
    }
    visited.insert(canonical.clone());

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

    // Extract module metadata using AST
    let (imports, exports, reexports) = extract_module_bindings(&source);

    let parent_dir = path.parent().unwrap_or(Path::new("."));

    // Collect dependencies first (topological order)
    for import in &imports {
        if import.source_path.starts_with("./") || import.source_path.starts_with("../") {
            let resolved = resolve_import(&import.source_path, parent_dir)?;
            collect_modules(&resolved, visited, modules, path_to_var)?;
        }
    }
    for reexport in &reexports {
        if reexport.source_path.starts_with("./") || reexport.source_path.starts_with("../") {
            let resolved = resolve_import(&reexport.source_path, parent_dir)?;
            collect_modules(&resolved, visited, modules, path_to_var)?;
        }

View on GitHub (pinned to 67894ca546)