xai-org/grok-build · error · std::io::Error

workflow artifact exceeds {limit} bytes: {}

Error message

workflow artifact exceeds {limit} bytes: {}

What it means

read_bounded_nofollow enforces a maximum artifact size. If symlink_metadata reports the file is larger than `limit` before opening, it returns InvalidData with this message to avoid unbounded memory use when loading run state.

Source

Thrown at crates/codegen/xai-grok-shell/src/session/workflow/store.rs:319

}

pub(crate) fn script_revision_path(run_dir: &Path, revision: u32) -> PathBuf {
    run_dir.join("scripts").join(format!("{revision:04}.rhai"))
}

pub(crate) fn read_bounded_nofollow(path: &Path, limit: u64) -> io::Result<Vec<u8>> {
    let metadata = std::fs::symlink_metadata(path)?;
    if metadata.file_type().is_symlink() || !metadata.is_file() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!(
                "workflow artifact is not a regular file: {}",
                path.display()
            ),
        ));
    }
    if metadata.len() > limit {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!(
                "workflow artifact exceeds {limit} bytes: {}",
                path.display()
            ),
        ));
    }
    let mut options = std::fs::OpenOptions::new();
    options.read(true);
    #[cfg(unix)]
    {
        use std::os::unix::fs::OpenOptionsExt;
        options.custom_flags(libc::O_NOFOLLOW);
    }
    let file = options.open(path)?;
    let opened = file.metadata()?;
    if !opened.is_file() || opened.len() > limit {
        return Err(io::Error::new(

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Raise the artifact size limit to a value that accommodates legitimate run state
  2. Inspect and truncate/compact the oversized artifact, then re-persist it
  3. Delete the corrupt/oversized run directory and re-register the run
  4. Find what wrote unbounded data (missing bounds in a writer) and fix it

Example fix

// before
let bytes = store.read_bounded_nofollow(&path, 1024)?; // legit manifest is 4 KB
// after
const MAX_RUN_MANIFEST: u64 = 64 * 1024;
let bytes = store.read_bounded_nofollow(&path, MAX_RUN_MANIFEST)?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn within_limit(p: &std::path::Path, limit: u64) -> bool {
    std::fs::symlink_metadata(p).map(|m| m.len() <= limit).unwrap_or(false)
}

Try / catch

match store.read_bounded_nofollow(&path, limit) {
    Ok(bytes) => Ok(bytes),
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData
        && e.to_string().contains("exceeds") => {
        // compact or quarantine the oversized run, do not retry blindly
        quarantine_run_dir(&run_dir)?;
        Ok(Vec::new())
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: A workflow artifact (runs.json or a .rhai script) exceeds the configured byte limit at load time in load_workflow_runs_sync — e.g. corrupted writer, huge injected file, or a too-small limit for legitimately large runs.

Common situations: Run manifest grown beyond expectations after many updates; maliciously planted oversized file in the runs dir; operator lowered the limit after old runs already exceed it.

Related errors


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