affaan-m/ECC · error · ValueError

artifact SHA-256 mismatch

Error message

artifact SHA-256 mismatch

What it means

`_read_local` computes the SHA-256 of every byte it reads and, when the caller supplied `expected_hash`, rejects the artifact if the computed digest differs. This is the final integrity gate: it proves the file content is byte-identical to what was attested when the application request was created. A mismatch means the content changed even if size and timestamps happened to line up.

Solutions

  1. Recompute the artifact's SHA-256 (`hashlib.sha256(bytes).hexdigest()`) and regenerate the application request with the fresh digest, then retry.
  2. Verify with `shasum -a 256 <file>` which side is stale — the file or the recorded hash — and regenerate whichever is out of date.
  3. Re-download/rebuild the artifact from the trusted source so its content matches the pinned hash.
  4. Ensure the hash producer normalizes output (64 lowercase hex, no `sha256:` prefix) identically to what the pipeline records.

Example fix

// before
# request pinned hash of v1 artifact; v2 was rebuilt without refreshing request
// after
fresh = hashlib.sha256(Path("/out/artifact.json").read_bytes()).hexdigest()
write_request(artifact_path, sha256=fresh)
req = load_application_request("/out/request.json")
Defensive patterns

Strategy: validation

Validate before calling

import hashlib, os
def sha256_file(path: str) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):
            h.update(chunk)
    return h.hexdigest()
def assert_hash_matches(path: str, expected_hash: str) -> None:
    actual = sha256_file(path)
    if actual != expected_hash.lower().removeprefix("sha256:"):
        raise ValueError(f"hash mismatch for {path}: expected {expected_hash}, got {actual}")

Try / catch

try:
    req = load_application_request(p)
except ValueError as e:
    if str(e) == "artifact SHA-256 mismatch":
        write_request(p, sha256=sha256_file(p), size=os.path.getsize(p))  # re-pin metadata
        req = load_application_request(p)
    else:
        raise

Prevention

When it happens

Trigger: Calling `load_application_request`/`_artifact` where the recorded `expected_hash` was computed over an older or different version of the file; the file was modified in a way that preserves size (in-place byte edits, timestamp-preserving writes); the hash was recorded from the wrong artifact or with different normalization (e.g. uppercase/prefixed hex handled upstream).

Common situations: Rebuilding the artifact without refreshing the request document; hot-patching a file in place; copying artifacts between machines with a lossy transfer; recording the hash of a pre-processed file but shipping the post-processed one.

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 affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/4bf9cb36c18ed94d. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/integration.py:151

        digest, chunks, count = hashlib.sha256(), [], 0
        while data := os.read(descriptor, 65536):
            count += len(data)
            if count > expected_size:
                raise ValueError("artifact byte count exceeded during reading")
            digest.update(data)
            if parse_json:
                chunks.append(data)
        # Rewalk the named path: a pinned old directory fd can outlive a rename.
        fresh_parent = _parent_fd(path)
        try:
            after = os.stat(path.name, dir_fd=fresh_parent, follow_symlinks=False)
        finally:
            os.close(fresh_parent)
        if (_identity(before) != _identity(os.fstat(descriptor))
                or _identity(before) != _identity(after)):
            raise ValueError("artifact changed during reading")
        if expected_hash is not None and digest.hexdigest() != expected_hash:
            raise ValueError("artifact SHA-256 mismatch")
        return _load_json(b"".join(chunks)) if parse_json else None
    except (OSError, AttributeError) as exc:
        raise ValueError("local artifact unavailable or unsafe") from exc
    finally:
        if descriptor is not None:
            os.close(descriptor)
        if parent is not None:
            os.close(parent)


def load_application_request(path: str | Path) -> dict:
    """Load only a bounded resident request; never follow a config symlink."""
    value = _read_local(str(Path(path).absolute()), parse_json=True)
    if not isinstance(value, dict):
        raise ValueError("application request must be a JSON object")
    return value

View on GitHub (pinned to 8321021c54)