xai-org/grok-build · error

Failed to read '{path}': {e}

Error message

Failed to read '{path}': {e}

What it means

PromptSource::from_file wraps std::fs::read_to_string failures as "Failed to read '{path}': {e}". The prompt file could not be opened or decoded as UTF-8 (missing file, permission denied, directory passed, or invalid encoding).

Source

Thrown at crates/codegen/xai-grok-pager/src/headless/cli.rs:61

        if let Some(text) = single {
            Self::from_text(text)
                .map(Some)
                .map_err(|e| anyhow::anyhow!("--single: {e}"))
        } else if let Some(json_str) = prompt_json {
            Self::from_json(json_str)
                .map(Some)
                .map_err(|e| anyhow::anyhow!("--prompt-json: {e}"))
        } else if let Some(path) = prompt_file {
            Self::from_file(path).map(Some)
        } else {
            Ok(None)
        }
    }

    /// `.json` files are parsed as content blocks, everything else as text.
    pub fn from_file(path: &Path) -> anyhow::Result<Self> {
        let content = std::fs::read_to_string(path)
            .map_err(|e| anyhow::anyhow!("Failed to read '{}': {e}", path.display()))?;

        let context = |e| anyhow::anyhow!("'{}': {e}", path.display());
        if path.extension().and_then(|e| e.to_str()) == Some("json") {
            Self::from_json(&content).map_err(context)
        } else {
            Self::from_text(&content).map_err(context)
        }
    }

    fn from_text(text: &str) -> anyhow::Result<Self> {
        let trimmed = text.trim();
        if trimmed.is_empty() {
            anyhow::bail!("prompt is empty");
        }
        Ok(Self::Text(trimmed.to_string()))
    }

    fn from_json(json_str: &str) -> anyhow::Result<Self> {

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Verify the path exists and is readable (ls -l / test -r)
  2. Use an absolute path or resolve relative to the script's directory
  3. Re-save the file as UTF-8 without BOM
  4. Check the inner {e} (NotFound/PermissionDenied/InvalidData) for the exact cause

Example fix

# before
pager --prompt-file prompts/task.txt
# after
pager --prompt-file "$(pwd)/prompts/task.txt"
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_readable_prompt_file(path: &std::path::Path) -> Result<(), String> {
    if !path.is_file() { return Err(format!("not a file: {}", path.display())); }
    let meta = std::fs::metadata(path).map_err(|e| e.to_string())?;
    if meta.len() == 0 { return Err("file is empty".into()); }
    std::fs::read_to_string(path).map(|_| ()).map_err(|e| e.to_string())
}

Type guard

fn is_readable_utf8_file(p: &std::path::Path) -> bool {
    p.is_file() && std::fs::read_to_string(p).is_ok()
}

Try / catch

match PromptSource::from_file(&path) {
    Ok(src) => src,
    Err(e) => {
        if e.to_string().contains("Failed to read") {
            eprintln!("prompt file unreadable: {e:#}"); std::process::exit(2);
        }
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Calling --prompt-file with a path that does not exist, is unreadable (permissions), is a directory, or contains non-UTF-8 bytes.

Common situations: Typo in the path; running from a different working directory with a relative path; CI checkout missing the file; file saved with UTF-16/BOM from a Windows editor.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/c3a590da97a4d1c6. Report an issue: GitHub.