Version or commit
957c9b1
Environment
Linux x86_64, Python via uv; library + CLI repro
Minimal reproduction
On main @ 957c9b1, verify_dataset_snapshot flattens receipt entries with:
# src/hflow/snapshot.py ~997–1002
*integrity.get("tables", {}).values(),
*integrity.get("assets", []),
.get(key, default) does not apply when the key is present with JSON null, so a hand-edited (or tampered) format.json crashes before the typed entry parser runs.
- Export a normal references-mode snapshot (any one-episode catalog + manifest).
- Edit
format.json so integrity.tables is null (leave a plausible content_id / assets):
"integrity": {
"content_id": "<any 64-hex or the original>",
"tables": null,
"assets": []
}
- Run:
uv run hflow verify snapshot <snapshot-dir>
# or: verify_dataset_snapshot(<snapshot-dir>)
Observed: AttributeError: 'NoneType' object has no attribute 'values' (traceback). The CLI only catches ValueError / path errors (src/hflow/cli.py ~1526–1529), so this is not exit 2.
Same class of crash:
| Mutation |
Observed |
"tables": null |
AttributeError (.values) |
"assets": null |
TypeError (spread non-iterable) |
"tables": [] |
AttributeError (list has no .values) |
Related (optional same fix): "assets": {} iterates object keys as fake entries and can surface a confusing content_id mismatch instead of “assets must be a JSON array”.
Not covered by the current open board (issues): #573, #568, #567, #565, #545 (PR #574), #528, #498, #491 (PR #517), #481 (PR #542), #287, #246. Same verify/receipt family as shipped #457 / #469 / #473 / #489, but this .get/.values() hole was never pinned.
Expected behavior
Malformed integrity.tables / integrity.assets container types (null, array-where-object, object-where-array) raise ValueError with a clear message (CLI exit 2), matching the docstring’s “refuse bad receipt shapes at the boundary” contract and the per-entry _parse_file_integrity_record style. No AttributeError/TypeError traceback.
Actual behavior
Present-but-null (or wrong-type) containers bypass the default and crash inside the flatten listcomp. Recipients / CI see an unhandled exception instead of a clean unreadable-input exit.
Additional context
Definition of done
- Before flattening, require
integrity.tables to be a JSON object and integrity.assets to be a JSON array (missing key may keep today’s default empty object/list); otherwise ValueError.
hflow verify snapshot returns exit 2 with a short message (no traceback) for those shapes.
- Tests in
tests/test_snapshot_verify.py (or sibling): tables: null, assets: null, tables: [] (and optionally assets: {}).
Non-goals
Version or commit
957c9b1
Environment
Linux x86_64, Python via uv; library + CLI repro
Minimal reproduction
On
main@957c9b1,verify_dataset_snapshotflattens receipt entries with:.get(key, default)does not apply when the key is present with JSONnull, so a hand-edited (or tampered)format.jsoncrashes before the typed entry parser runs.format.jsonsointegrity.tablesisnull(leave a plausiblecontent_id/assets):Observed:
AttributeError: 'NoneType' object has no attribute 'values'(traceback). The CLI only catchesValueError/ path errors (src/hflow/cli.py~1526–1529), so this is not exit2.Same class of crash:
"tables": nullAttributeError(.values)"assets": nullTypeError(spread non-iterable)"tables": []AttributeError(list has no.values)Related (optional same fix):
"assets": {}iterates object keys as fake entries and can surface a confusingcontent_idmismatch instead of “assets must be a JSON array”.Not covered by the current open board (issues): #573, #568, #567, #565, #545 (PR #574), #528, #498, #491 (PR #517), #481 (PR #542), #287, #246. Same verify/receipt family as shipped #457 / #469 / #473 / #489, but this
.get/.values()hole was never pinned.Expected behavior
Malformed
integrity.tables/integrity.assetscontainer types (null, array-where-object, object-where-array) raiseValueErrorwith a clear message (CLI exit2), matching the docstring’s “refuse bad receipt shapes at the boundary” contract and the per-entry_parse_file_integrity_recordstyle. No AttributeError/TypeError traceback.Actual behavior
Present-but-null (or wrong-type) containers bypass the default and crash inside the flatten listcomp. Recipients / CI see an unhandled exception instead of a clean unreadable-input exit.
Additional context
Definition of done
integrity.tablesto be a JSON object andintegrity.assetsto be a JSON array (missing key may keep today’s default empty object/list); otherwiseValueError.hflow verify snapshotreturns exit2with a short message (no traceback) for those shapes.tests/test_snapshot_verify.py(or sibling):tables: null,assets: null,tables: [](and optionallyassets: {}).Non-goals
3unverifiable vs exit2for a wholly non-objectintegrityvalue (can be a follow-up; todayintegrity: []reportsno-receipt/ exit 3).