jq Error: 'Cannot iterate over null (null)' — Cause, Fix, and Troubleshooting Guide
Fix jq error: Cannot iterate over null (null) — a missing key, wrong path, empty API response, or wrong nesting feeds null into .[] iteration.
- #automation
- #troubleshooting
- #jq
Stuck on this Automation error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
jq raises Cannot iterate over null (null) when you ask it to iterate — with .[], [.foo[]], map(), or a pipe into an array operation — over a value that is null instead of an array or object. In jq, looking up a key that does not exist returns null (it does not error), and null cannot be iterated, so the failure surfaces one step later at the iteration.
$ curl -s https://api.example.com/v1/items | jq '.data[] | .id'
jq: error (at <stdin>:0): Cannot iterate over null (null)
The message is almost never about the data being genuinely empty — it is about a path mismatch. The key you indexed (.data) does not exist in the JSON (maybe it is .results, or the payload is nested differently, or the request returned an error object), so .data evaluated to null, and null[] is what jq refused to iterate. The exit status is non-zero (5), which fails the surrounding script.
Symptoms
jq: error (at <stdin>:N): Cannot iterate over null (null).- jq exits non-zero (5), breaking the pipeline or CI step.
- The raw JSON looks fine to the eye, but the specific path you indexed is absent.
- Works against one API response and fails against another (empty result, error body, or different schema).
- Intermittent: passes when data is present, fails when the list is empty or the endpoint returns an error object.
curl -s https://api.example.com/v1/items | jq '.data[]' >/dev/null; echo "exit: $?"
exit: 5
Common Root Causes
1. The key does not exist (typo or wrong name)
.data when the field is actually .results — the lookup returns null, then .[] fails.
curl -s https://api.example.com/v1/items | jq 'keys'
[
"results",
"page",
"total"
]
2. Wrong nesting level
The array is one level deeper (or shallower) than the expression assumes, e.g. .data.items[] vs .data[].
3. Empty or error API response
The endpoint returned {}, an error object like {"error":"not found"}, or nothing — so the expected array key is absent.
4. Iterating a value that is legitimately null for this record
An optional field (.tags[]) is null on some records and a list on others; the run fails the first time it hits a null.
5. Response is a JSON array at the top level, not an object
If the body is already [...], then .data is wrong — you want .[] directly.
6. HTTP error body captured instead of JSON
A 4xx/5xx returned HTML or a plain-text error that either fails to parse or lacks the key.
How to Diagnose
Step 1: Look at the actual shape before indexing
curl -s https://api.example.com/v1/items | jq 'type, keys?'
"object"
[
"results",
"page",
"total"
]
The top level is an object whose keys are results, page, total — there is no data, which is exactly why .data was null.
Step 2: Confirm the offending path is null
curl -s https://api.example.com/v1/items | jq '.data'
null
null here proves the path, not the data, is the problem.
Step 3: Capture the raw body in case it is not what you expect
curl -s -w '\nHTTP %{http_code}\n' https://api.example.com/v1/items | tail -5
{"error":"not found"}
HTTP 404
A 404 error object explains the missing array entirely.
Step 4: Check the top-level type (object vs array)
curl -s https://api.example.com/v1/items | jq 'if type=="array" then "top-level array" else "object" end'
Fixes
Use the correct path once you know the real key:
curl -s https://api.example.com/v1/items | jq '.results[] | .id'
Make the expression null-safe so an empty or missing list yields no output instead of an error. // supplies a default, and ? suppresses iteration errors:
# Default a missing/null value to an empty array
curl -s https://api.example.com/v1/items | jq '(.data // [])[] | .id'
# Or tolerate the error and emit nothing
curl -s https://api.example.com/v1/items | jq '.data[]? | .id'
Both approaches let a legitimately empty response pass cleanly:
echo '{}' | jq '(.data // [])[]'; echo "exit: $?"
exit: 0
Guard the HTTP status in the script so an error body never reaches jq:
resp=$(curl -s -w '\n%{http_code}' https://api.example.com/v1/items)
code=$(printf '%s' "$resp" | tail -1)
body=$(printf '%s' "$resp" | sed '$d')
[ "$code" = "200" ] || { echo "HTTP $code: $body" >&2; exit 1; }
printf '%s' "$body" | jq '.results[] | .id'
What to Watch Out For
Cannot iterate over nullis a path bug far more often than an empty data bug — inspectkeysbefore assuming the API changed.- jq returns
nullfor a missing key rather than erroring, so the failure appears one operation downstream at the iteration. .[]?and(.x // [])are the idiomatic guards, but do not sprinkle them everywhere — a silenced error can hide a genuinely broken response.- An HTTP 4xx/5xx body is still fed to jq unless you check the status first; a non-200 error object is a common trigger.
- Distinguish a top-level array (
jq '.[]') from an object with an array field (jq '.data[]') — checktypewhen unsure.
Related Guides
- yq: bad file / YAML mapping values are not allowed
- 429 Too Many Requests — downstream rate limit
- n8n node execution failed: ECONNRESET
Fixed it? Get 500 Automation & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3mount: wrong fs type, bad option, bad superblock
- 4Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block
- 5Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 6modprobe: FATAL: Module not found
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.