{"record":{"id":"88659fb705cd73df","repo":"affaan-m/ECC","slug":"receipt-reference-has-an-invalid-finite-source-duration","errorCode":null,"errorMessage":"receipt reference has an invalid finite source duration","messagePattern":"receipt reference has an invalid finite source duration","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":334,"sourceCode":"    source_durations: dict[tuple[str, str], float] = {}\n    for key in (\"references\", \"evidence_files\"):\n        sources = receipt.get(key, [])\n        if not isinstance(sources, list):\n            raise ContractError(f\"receipt {key} must be a list\")\n        for source in sources:\n            if not isinstance(source, dict):\n                raise ContractError(f\"receipt {key} contains an invalid source\")\n            source_path = source.get(\"path\")\n            expected_digest = source.get(\"sha256\")\n            if (not isinstance(source_path, str) or not source_path\n                    or not isinstance(expected_digest, str)\n                    or not re.fullmatch(r\"[0-9a-f]{64}\", expected_digest)):\n                raise ContractError(\"receipt has an invalid source identity\")\n            known_sources.add((source_path, expected_digest))\n            if key == \"references\":\n                source_duration = source.get(\"source_duration\")\n                if not _is_finite_real(source_duration):\n                    raise ContractError(\"receipt reference has an invalid finite source duration\")\n                source_duration = cast(float, source_duration)\n                if float(source_duration) <= 0:\n                    raise ContractError(\"receipt reference has an invalid finite source duration\")\n                source_durations[(source_path, expected_digest)] = float(source_duration)\n                _validate_probe_evidence(\n                    source.get(\"probe\"), float(source_duration), label=\"receipt reference\"\n                )\n    source_policy = receipt.get(\"source_availability_policy\")\n    if known_sources and source_policy not in {\"allow_unavailable\", \"require_available\"}:\n        raise ContractError(\"receipt must declare an explicit source availability policy\")\n    for source_path, expected_digest in sorted(known_sources):\n        path = Path(source_path)\n        try:\n            metadata = path.lstat()\n        except FileNotFoundError:\n            if source_policy == \"require_available\":\n                raise ContractError(f\"receipt source is unavailable: {source_path}\") from None\n            continue","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L316-L352","documentation":"Receipt \"references\" entries must include a \"source_duration\" that is a finite real number (int or float, not NaN/inf, not bool). If _is_finite_real rejects the value, ContractError('receipt reference has an invalid finite source duration') is raised. The duration anchors probe-evidence validation performed immediately after.","triggerScenarios":"A reference entry whose \"source_duration\" is missing, null, a string like \"3.5\", a bool, NaN, or +/-Infinity.","commonSituations":"Duration serialized as a string by a custom serializer; JSON produced with NaN/Infinity via non-strict dumps; references copied from \"evidence_files\" entries which do not carry durations; hand-written receipts omitting the field.","solutions":["Add a numeric (int/float) \"source_duration\" > 0 to every entry under \"references\"","Convert string durations with float(value) before building the receipt","Fix the JSON emitter to use allow_nan=False and emit real numbers","If the entry is evidence rather than a reference, move it to \"evidence_files\" where duration is not required"],"exampleFix":"// before\n{\"path\": \"ref.csv\", \"sha256\": \"..\", \"source_duration\": \"12.5\"}\n// after\n{\"path\": \"ref.csv\", \"sha256\": \"..\", \"source_duration\": 12.5}","handlingStrategy":"validation","validationCode":"import math\n\ndef duration_ok(entry):\n    d = entry.get('source_duration')\n    return isinstance(d, (int, float)) and not isinstance(d, bool) and math.isfinite(d)","typeGuard":"def is_finite_real(v) -> bool:\n    return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v)","tryCatchPattern":"try:\n    validate_artifact_receipt(receipt, out_dir)\nexcept ContractError as e:\n    if 'invalid finite source duration' in str(e):\n        for s in receipt['references']:\n            s['source_duration'] = float(s['source_duration'])\n    raise","preventionTips":["Store durations as JSON numbers, never strings","Serialize with json.dumps(..., allow_nan=False) to block NaN/Infinity","Coerce numeric strings to float when building receipts","Only put source_duration on references, not evidence_files"],"tags":["validation","receipt","duration"],"backgroundTag":"invalid-argument-value","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"}