> ## Documentation Index
> Fetch the complete documentation index at: https://gtmsdk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fix: enrichment fetch/upsert exit cleanly on wrong-shape JSON input files

> The fetch and upsert loaders guarded missing files, invalid JSON, and the empty case, but never the parsed shape — a valid-JSON object, or an array of non-objects, crashed both commands with raw tracebacks. Both now validate the file's shape and exit 1 with a friendly error.

## 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.