{"record":{"id":"2c131f749bf8c1a1","repo":"affaan-m/ECC","slug":"artifact-byte-count-mismatch","errorCode":null,"errorMessage":"artifact byte count mismatch","messagePattern":"artifact byte count mismatch","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/integration.py","lineNumber":129,"sourceCode":"\ndef _read_local(raw: str, *, parse_json: bool, expected_size: int | None = None,\n                expected_hash: str | None = None) -> Any:\n    path = Path(raw)\n    if not path.is_absolute() or str(path) != raw or \"..\" in path.parts:\n        raise ValueError(\"artifact path must be canonical and absolute\")\n    parent = descriptor = None\n    try:\n        flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK\n        parent = _parent_fd(path)\n        before = os.stat(path.name, dir_fd=parent, follow_symlinks=False)\n        if not stat.S_ISREG(before.st_mode) or getattr(before, \"st_flags\", 0) & 0x40000000:\n            raise ValueError(\"artifact must be a resident regular file\")\n        if expected_size is None:\n            expected_size = before.st_size\n        if parse_json and expected_size > _MAX_JSON:\n            raise ValueError(\"JSON artifact exceeds local size limit\")\n        if before.st_size != expected_size:\n            raise ValueError(\"artifact byte count mismatch\")\n        descriptor = os.open(path.name, flags, dir_fd=parent)\n        if _identity(before) != _identity(os.fstat(descriptor)):\n            raise ValueError(\"artifact changed before reading\")\n        digest, chunks, count = hashlib.sha256(), [], 0\n        while data := os.read(descriptor, 65536):\n            count += len(data)\n            if count > expected_size:\n                raise ValueError(\"artifact byte count exceeded during reading\")\n            digest.update(data)\n            if parse_json:\n                chunks.append(data)\n        # Rewalk the named path: a pinned old directory fd can outlive a rename.\n        fresh_parent = _parent_fd(path)\n        try:\n            after = os.stat(path.name, dir_fd=fresh_parent, follow_symlinks=False)\n        finally:\n            os.close(fresh_parent)\n        if (_identity(before) != _identity(os.fstat(descriptor))","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/integration.py#L111-L147","documentation":"`_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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n# request.json has expected_size=1024, but artifact.json is now 1188 bytes after a rebuild\n_read_local(p, parse_json=True, expected_size=1024)\n// after\ninfo = os.stat(p)  # refresh metadata alongside a fresh hash\n_read_local(p, parse_json=True, expected_size=info.st_size, expected_hash=fresh_digest)","handlingStrategy":"validation","validationCode":"import os, hashlib\ndef assert_size_matches(path: str, expected_size: int) -> None:\n    actual = os.path.getsize(path)\n    if actual != expected_size:\n        raise ValueError(f\"size drift: recorded {expected_size}, on disk {actual}\")","typeGuard":null,"tryCatchPattern":"try:\n    req = load_application_request(p)\nexcept ValueError as e:\n    if str(e) == \"artifact byte count mismatch\":\n        info = os.stat(p)\n        digest = sha256_file(p)\n        write_request(p, size=info.st_size, sha256=digest)  # refresh pinned metadata\n        req = load_application_request(p)\n    else:\n        raise","preventionTips":["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."],"tags":["filesystem","integrity","size-mismatch"],"backgroundTag":"checksum-mismatch","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}