xai-org/grok-build · error

'{path}': {e}

Error message

'{path}': {e}

What it means

After a successful read, from_file parses the content with a context closure |e| anyhow!("'{path}': {e}"): .json files go through from_json (content blocks), everything else through from_text. This error means the file content was read but failed prompt parsing/validation, prefixed with the file path.

Source

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

                .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> {
        let blocks = parse_prompt_json(json_str)?;
        Ok(Self::Blocks(blocks))

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Read the inner {e} after the path prefix for the parse cause
  2. For JSON content ensure it matches the content-block schema exactly
  3. Rename plain-text files to .txt (or drop .json) so they parse as text
  4. Ensure the file is non-empty and correctly encoded

Example fix

// before: prompts/req.json containing plain text "do the thing"
// after: rename to prompts/req.txt or convert to content blocks
[{"type": "text", "text": "do the thing"}]
Defensive patterns

Strategy: validation

Validate before calling

fn validate_prompt_file_content(path: &std::path::Path) -> Result<(), String> {
    let content = std::fs::read_to_string(path).map_err(|e| e.to_string())?;
    if content.trim().is_empty() { return Err("prompt file is empty".into()); }
    if path.extension().and_then(|e| e.to_str()) == Some("json") {
        serde_json::from_str::<serde_json::Value>(&content)
            .map(|_| ()).map_err(|e| format!("invalid JSON in {}: {e}", path.display()))?;
    }
    Ok(())
}

Type guard

fn prompt_file_parses(path: &std::path::Path) -> bool {
    std::fs::read_to_string(path).ok()
        .map(|c| if path.extension().and_then(|e| e.to_str()) == Some("json") {
            serde_json::from_str::<serde_json::Value>(&c).is_ok() && !c.trim().is_empty()
        } else { !c.trim().is_empty() })
        .unwrap_or(false)
}

Try / catch

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

Prevention

When it happens

Trigger: from_file given a .json file whose content is not valid prompt content-block JSON, or a non-.json file whose text content fails from_text validation (e.g. empty content).

Common situations: A .json prompt file that is actually plain text (wrong extension); an empty prompt file; content-block JSON copied from a different tool/version with an incompatible schema.

Related errors


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