xai-org/grok-build · error

InvalidInput

InvalidInput

Error message

path has no parent

What it means

`atomic_write` in the subagent module persists `meta.json`/`output.json` by writing a same-directory temp file and renaming. If `path.parent()` returns `None` — the path is bare/root-like with no directory component — the function fails with `std::io::Error` of kind `InvalidInput` and message 'path has no parent', because the temp file must be created in the same directory as the target for the rename to be atomic.

Source

Thrown at crates/codegen/xai-grok-shell/src/agent/subagent/mod.rs:2294

            isolation_mode: isolation_mode.map(str::to_string),
            depth,
            started_at: meta.started_at.to_rfc3339(),
            completed_at: meta.completed_at.map(|t| t.to_rfc3339()),
            status: meta.status.clone(),
            duration_ms: meta.duration_ms,
            tool_calls: meta.tool_calls,
            turns: meta.turns,
            error: meta.error.clone(),
            fork_copy_error: meta.fork_copy_error.clone(),
            resumed_from: meta.resumed_from.clone(),
        }
    }
}
/// Write via a same-directory temp file and rename, so a crash mid-write
/// cannot leave a torn `meta.json` or `output.json`.
fn atomic_write(path: &Path, contents: &str) -> std::io::Result<()> {
    let parent = path.parent().ok_or_else(|| {
        std::io::Error::new(std::io::ErrorKind::InvalidInput, "path has no parent")
    })?;
    std::fs::create_dir_all(parent)?;
    let tmp = tempfile::NamedTempFile::new_in(parent)?;
    std::fs::write(tmp.path(), contents)?;
    tmp.persist(path)?;
    Ok(())
}
/// Write `meta.json`. Returns `true` on success so callers on the resume-pointer
/// path can gate worktree disposal on a durable write.
fn write_subagent_meta(dir: &Path, meta: &SubagentMeta) -> bool {
    let json = match serde_json::to_string_pretty(meta) {
        Ok(json) => json,
        Err(e) => {
            tracing::warn!(error = %e, "failed to serialize subagent meta");
            return false;
        }
    };
    if let Err(e) = atomic_write(&dir.join("meta.json"), &json) {

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Always pass an absolute path under the subagent's directory (e.g. `<session_dir>/meta.json`)
  2. Join the bare filename onto a real base directory before writing
  3. Pre-validate `path.parent().is_some()` in the caller and error out early with a better message

Example fix

// before
atomic_write(Path::new("meta.json"), &contents)?;
// after
let path = session_dir.join("meta.json");
atomic_write(&path, &contents)?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_writable(p: &Path) -> Result<(), String> {
    if p.parent().is_none() {
        return Err(format!("{}: join the filename onto a real directory", p.display()));
    }
    Ok(())
}

Type guard

fn has_parent_dir(p: &Path) -> bool { p.parent().is_some() }

Try / catch

match atomic_write(&path, &contents) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string() == "path has no parent" => {
        eprintln!("meta/output path must include its directory");
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `atomic_write` (via subagent meta/output persistence) with a path like `Path::new("meta.json")` or `Path::new("/")` that has no parent directory component.

Common situations: A bug where the subagent directory prefix was empty or dropped before joining the filename; constructing the path from a mis-parsed config value; tests passing a bare filename directly.

Related errors


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