Bug fixes
Enrichment CLI crash on valid-JSON wrong-shape input files (issue #882)
gtm enrichment enrich fetch --records and gtm enrichment enrich upsert --input both read a user-supplied JSON file, and both loaders handled FileNotFoundError, json.JSONDecodeError, and the empty-list case with friendly ❌/⚠️ messages and clean exits — but never validated the parsed shape: neither the top-level type nor that array items were objects. A valid-JSON object sailed past every guard: the empty-case check (if not records_data) passes because a non-empty dict is truthy, and the commands then crashed downstream. fetch raised an AttributeError inside build_enrichment_tasks (iterating a dict yields its string keys); upsert --dry-run raised a TypeError in its unguarded echo loop; and non-dry-run upsert was worst — its per-record error handler indexed the same bad item it had just caught, chaining a second TypeError into a double traceback. An array of non-objects ([42], ["record"]) crashed identically, since task building calls row.get(...) on each item and the upsert loops index into it.
Both loaders now validate the file’s shape immediately after parsing, mirroring their existing friendly-error pattern. The top-level value must be a JSON array (❌ Records file must contain a JSON array / ❌ Enriched file must contain a JSON array), and every array item must be a JSON object (❌ … array items must all be JSON objects). All rejections exit 1 before any API-key check, Attio call, or output write. An empty object is rejected too — only [] remains the valid-empty case (warning, exit 0). The documented pipeline is unaffected: fetch always writes a JSON array of objects, so upsert only sees a wrong-shape file when it is hand-edited or produced outside the pipeline.Last modified on October 2, 2026