sinelaw/fresh · error
could not read script
Error message
could not read script '{}': {} What it means
Raised while reading a script for a CLI subcommand: stdin ("-")/no-arg reads stdin, but an explicit path goes through `std::fs::read_to_string`, and any I/O failure is wrapped as `could not read script '{}': {}` with the OS error appended. The wrapper tells you which path failed and why (missing, permission, not a file, invalid UTF-8).
Solutions
- Check the path exists and is spelled correctly (ls the exact path).
- Pipe the script via stdin instead: pass '-' or omit the path and use `... < script`.
- Fix file permissions (chmod/chown) or run as a user with read access.
- Re-save the script as UTF-8 — read_to_string rejects non-UTF-8 bytes.
Example fix
// before: failing on a possibly-missing path
let src = std::fs::read_to_string(path).map_err(|e| anyhow::anyhow!("could not read script '{}': {}", path, e))?;
// after: validate before reading and give a targeted message
if !std::path::Path::new(path).is_file() {
anyhow::bail!("script '{}' does not exist or is not a file", path);
}
let src = std::fs::read_to_string(path).map_err(|e| anyhow::anyhow!("could not read script '{}': {}", path, e))?; Defensive patterns
Strategy: validation
Validate before calling
fn readable_script(path: &str) -> Result<(), String> {
let p = std::path::Path::new(path);
if !p.is_file() { return Err(format!("'{}' is not a file", path)); }
std::fs::File::open(p).map_err(|e| e.to_string())?;
Ok(())
} Try / catch
match read_script(path) {
Err(e) if e.to_string().starts_with("could not read script") => {
eprintln!("check the path, permissions, and that the file is UTF-8");
}
other => other?,
} Prevention
- Use absolute paths for scripts to avoid cwd surprises
- Save scripts as UTF-8
- Stat the file before invoking the CLI
- Fall back to stdin ('-' or piping) when the path is uncertain
When it happens
Trigger: Passing a script file path to the script-taking subcommand when the file cannot be read: nonexistent path, no read permission, it's a directory, or it contains invalid UTF-8.
Common situations: Typo in the script path; relative path resolved from the wrong working directory; script written with a non-UTF-8 encoding; running under a user lacking read permission.
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
- Too many '+ ' arguments (at most one is allowed)
- '+ ' requires a file argument (e.g. 'fresh + file.txt'…
- Cannot open files from multiple remote hosts. First
- Cannot mix local and remote files. Use either local paths…
- No data piped to stdin
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/30512712c6e13b37.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/main.rs:4682
}
std::process::exit(1);
}
println!("ok");
Ok(())
}
/// Read a script from a file argument, or stdin when absent or `-`.
fn read_script_source(from: &[&str]) -> AnyhowResult<String> {
use std::io::Read;
match from.first().copied() {
None | Some("-") => {
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
Ok(buf)
}
Some(path) => std::fs::read_to_string(path)
.map_err(|e| anyhow::anyhow!("could not read script '{}': {}", path, e)),
}
}
/// Attach to an existing daemon, starting one if needed
fn run_attach_command(args: &Args) -> AnyhowResult<()> {
run_attach(
args.session_name.as_deref(),
&args.files,
args.locale.as_deref(),
args.config.as_deref(),
)
}
/// `locale` and `config` are the client's own `--locale` and `--config`,
/// forwarded to a daemon we *start* here so those flags survive the hop
/// into the process that actually renders the UI and reads the config.
/// Attaching to an already-running daemon leaves both alone: it is someone
/// else's session, already serving other terminals, the same way anView on GitHub (pinned to 67894ca546)