Hmbown/CodeWhale · error

Fleet artifact size changed

Error message

Fleet artifact size changed

What it means

read_verified compares the artifact's actual on-disk size with the size_bytes recorded in the FleetArtifactRef receipt before reading. If the recorded expectation exists and differs from the current file length, verification fails — the artifact changed after its receipt was issued and can no longer be trusted as the evidence that was published.

Solutions

  1. Re-capture the FleetArtifactRef (re-run publication) so size and checksum match the current file.
  2. Find and stop the process that rewrote the artifact after publication — publication is meant to be immutable.
  3. Restore the original file bytes from a backup if the current file is the corrupted copy.
  4. If the receipt is stale from an old run, discard it and reference the receipt from the latest run.
Defensive patterns

Strategy: validation

Validate before calling

let actual = fs::metadata(ws.join(&artifact.path))?.len();
if let Some(expected) = artifact.size_bytes {
    if actual != expected {
        return Err("artifact changed since receipt; re-publish before verifying");
    }
}

Prevention

When it happens

Trigger: Calling read_verified with a FleetArtifactRef whose checksum/size receipt was computed for an earlier version of the file, but the file has since been rewritten, appended to, or truncated by another process.

Common situations: A run republished to the same path after the receipt was captured (defeating immutability by external write); concurrent writers racing on one artifact path; a stale receipt referenced after a workspace regeneration.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/940ca05187084d62. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/artifacts.rs:58

            Ok(())
        }
        Err(error) => Err(error).context("Publishing Fleet artifact"),
    }
}

pub(crate) fn read_verified(
    workspace: &Path,
    artifact: &FleetArtifactRef,
    preview_limit: u64,
) -> Result<(Vec<u8>, u64)> {
    let parent = WorkspaceFile::open(workspace, &artifact.path, false)?;
    let file = parent.open_file()?;
    let size = file.metadata()?.len();
    ensure!(
        size <= MAX_ARTIFACT_BYTES,
        "Fleet artifact exceeds the 16 MiB verification limit"
    );
    ensure!(
        artifact.size_bytes.is_none_or(|expected| expected == size),
        "Fleet artifact size changed"
    );
    let checksum = artifact
        .checksum
        .as_deref()
        .context("Fleet artifact has no recorded checksum")?;
    let mut hasher = Sha256::new();
    let mut preview = Vec::new();
    let mut buffer = [0_u8; 8192];
    let mut total = 0_u64;
    // The digest and returned preview consume exactly the same bytes from the
    // same opened file. A changed/replaced pathname is never reopened for data.
    let mut reader = (&file).take(size + 1);
    loop {
        let count = reader.read(&mut buffer)?;
        if count == 0 {
            break;

View on GitHub (pinned to 73e0f67d83)