{"record":{"id":"19710a89627c53f3","repo":"affaan-m/ECC","slug":"artifact-relative-has-invalid-reference-sha-256","errorCode":null,"errorMessage":"artifact {relative} has invalid reference SHA-256","messagePattern":"artifact (.+?) has invalid reference SHA-256","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":414,"sourceCode":"        if not isinstance(entry.get(\"genre_numbers\"), list):\n            raise ContractError(f\"artifact {relative} lacks genre binding\")\n        modalities = entry.get(\"modalities\")\n        if (not isinstance(modalities, list)\n                or any(modality not in _REQUIRED_MODALITIES for modality in modalities)):\n            raise ContractError(f\"artifact {relative} has invalid modality binding\")\n        if entry.get(\"bytes\") != path.stat().st_size:\n            raise ContractError(f\"artifact {relative} byte size does not match receipt\")\n        if entry.get(\"sha256\") != _sha256(path):\n            raise ContractError(f\"artifact {relative} SHA-256 does not match receipt\")\n        provenance = entry.get(\"provenance\")\n        if not isinstance(provenance, list) or not provenance:\n            raise ContractError(f\"artifact {relative} lacks exact reference/time provenance\")\n        for source in provenance:\n            if not isinstance(source.get(\"reference_path\"), str) or not source[\"reference_path\"]:\n                raise ContractError(f\"artifact {relative} has invalid reference path\")\n            digest = source.get(\"reference_sha256\")\n            if not isinstance(digest, str) or len(digest) != 64:\n                raise ContractError(f\"artifact {relative} has invalid reference SHA-256\")\n            if (source[\"reference_path\"], digest) not in known_sources:\n                raise ContractError(f\"artifact {relative} cites an unknown provenance source\")\n            times = source.get(\"reference_times\")\n            basis = source.get(\"time_basis\")\n            if not isinstance(times, list) or basis not in {\"media_seconds\", \"whole_file\"}:\n                raise ContractError(f\"artifact {relative} has invalid reference/time provenance\")\n            if basis == \"media_seconds\" and not times:\n                raise ContractError(f\"artifact {relative} lacks media reference times\")\n            if basis == \"whole_file\" and times:\n                raise ContractError(f\"artifact {relative} whole-file provenance must not invent times\")\n            if basis == \"media_seconds\":\n                expected_duration = source_durations.get((source[\"reference_path\"], digest))\n                if expected_duration is None or source.get(\"source_duration\") != expected_duration:\n                    raise ContractError(f\"artifact {relative} has an unbound source duration\")\n                for time in times:\n                    _validate_media_time(\n                        time, expected_duration,\n                        label=f\"artifact {relative} media reference time\",","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L396-L432","documentation":"Each provenance source must include a 'reference_sha256' that is a string of exactly 64 hex characters (a SHA-256 digest). This error is raised when the digest is missing, non-string, or the wrong length. It lets the validator match the cited source against the set of known reference files.","triggerScenarios":"A provenance source whose reference_sha256 is absent, truncated (e.g. short hash), a full hash with 0x prefix, a non-string type, or computed with a different algorithm (MD5/SHA-1) producing the wrong length.","commonSituations":"Hand-editing receipts with abbreviated hashes; hashing with md5() instead of sha256(); copying hashes from other tooling formats; older receipts generated before 64-char digests were enforced.","solutions":["Recompute the reference file digest with hashlib.sha256(...).hexdigest() and store the full 64-char string","Verify no truncation or surrounding whitespace/prefix when pasting the hash","Regenerate the receipt through tasteforge so digests are computed correctly","Ensure the reference file itself still hashes to the recorded digest before validation"],"exampleFix":"import hashlib\n# before\n'reference_sha256': '3f2a1b'  # truncated\n# after\n'reference_sha256': hashlib.sha256(Path('refs/intro.mp4').read_bytes()).hexdigest()","handlingStrategy":"validation","validationCode":"import re\nd = src.get('reference_sha256')\nassert isinstance(d, str) and re.fullmatch(r'[0-9a-f]{64}', d), f'bad digest: {d!r}'","typeGuard":"def valid_sha256(value) -> bool:\n    return isinstance(value, str) and len(value) == 64 and all(c in '0123456789abcdef' for c in value)","tryCatchPattern":"try:\n    validate_artifact_receipt(out_dir)\nexcept ContractError as e:\n    if 'invalid reference SHA-256' in str(e):\n        recompute_reference_digests(receipt)\n    else:\n        raise","preventionTips":["Always hash with hashlib.sha256().hexdigest(), never md5/sha1","Paste full 64-char digests; never abbreviate","Recompute digests whenever a reference file changes","Add digest-format linting to receipt tooling"],"tags":["provenance","checksum","validation"],"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"}