affaan-m/ECC · error · ValueError
artifact byte count mismatch
Error message
artifact byte count mismatch
What it means
`_read_local` compares the file's size observed at the pre-open `lstat` against the caller-supplied `expected_size` (when one was provided, e.g. recorded alongside a hash in the application request). A mismatch means the artifact on disk is not byte-for-byte the size of the artifact that was originally attested, so the library refuses to read it. This is one of the integrity gates that makes hash pinning trustworthy.
Solutions
- Regenerate the application request (re-stat and re-hash the artifact) so `expected_size` matches the current file, then retry.
- Regenerate the artifact from the original build so its byte count matches the recorded size.
- If the recorded size came from a transfer step, re-transfer in binary mode and verify the size matches before loading.
- Audit the metadata producer to confirm it records `st_size` of the exact artifact being referenced, not a different variant.
Example fix
// before # request.json has expected_size=1024, but artifact.json is now 1188 bytes after a rebuild _read_local(p, parse_json=True, expected_size=1024) // after info = os.stat(p) # refresh metadata alongside a fresh hash _read_local(p, parse_json=True, expected_size=info.st_size, expected_hash=fresh_digest)
Defensive patterns
Strategy: validation
Validate before calling
import os, hashlib
def assert_size_matches(path: str, expected_size: int) -> None:
actual = os.path.getsize(path)
if actual != expected_size:
raise ValueError(f"size drift: recorded {expected_size}, on disk {actual}") Try / catch
try:
req = load_application_request(p)
except ValueError as e:
if str(e) == "artifact byte count mismatch":
info = os.stat(p)
digest = sha256_file(p)
write_request(p, size=info.st_size, sha256=digest) # refresh pinned metadata
req = load_application_request(p)
else:
raise Prevention
- Regenerate the application request immediately after every artifact rebuild — never reuse stale metadata.
- Always transfer artifacts in binary mode to avoid size-changing transformations.
- Verify recorded size AND hash together; they should come from the same stat/hash pass.
- Freeze the artifact (no further writes) before recording its metadata.
When it happens
Trigger: Passing `expected_size=N` to `_read_local` (via `_artifact` or `load_application_request`) for a file that has since been rewritten, truncated, appended to, or was recorded with the wrong size in the request document.
Common situations: A build regenerated the artifact after the request was written; a partial upload/download left a truncated file; the recorded metadata came from a different build or platform variant; CRLF-translating transfer changed the byte count.
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
- Nasiko archive size mismatch.
- Published Nasiko binary size mismatch.
- reference source mutated while creating stable snapshot
- Refusing to hash changed install destination
- all overlays must be readable local files
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/2c131f749bf8c1a1.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/integration.py:129
def _read_local(raw: str, *, parse_json: bool, expected_size: int | None = None,
expected_hash: str | None = None) -> Any:
path = Path(raw)
if not path.is_absolute() or str(path) != raw or ".." in path.parts:
raise ValueError("artifact path must be canonical and absolute")
parent = descriptor = None
try:
flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK
parent = _parent_fd(path)
before = os.stat(path.name, dir_fd=parent, follow_symlinks=False)
if not stat.S_ISREG(before.st_mode) or getattr(before, "st_flags", 0) & 0x40000000:
raise ValueError("artifact must be a resident regular file")
if expected_size is None:
expected_size = before.st_size
if parse_json and expected_size > _MAX_JSON:
raise ValueError("JSON artifact exceeds local size limit")
if before.st_size != expected_size:
raise ValueError("artifact byte count mismatch")
descriptor = os.open(path.name, flags, dir_fd=parent)
if _identity(before) != _identity(os.fstat(descriptor)):
raise ValueError("artifact changed before reading")
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))View on GitHub (pinned to 8321021c54)