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
- Read the inner {e} after the path prefix for the parse cause
- For JSON content ensure it matches the content-block schema exactly
- Rename plain-text files to .txt (or drop .json) so they parse as text
- 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
- Match file extension to content (.json only for content-block JSON)
- Reject empty prompt files in generation pipelines
- Round-trip generated JSON through a parser before writing
- Keep prompt files in version control so regressions are caught
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
- --json-schema: invalid JSON: {e}
- --single: {e}
- --prompt-json: {e}
- Invalid JSON: {e}
- unknown key notation: <{notation}>
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/12cec1b4f92128ef.
Report an issue: GitHub.