jq -e exits 4 when the filter outputs nothing: CI gates misfire
With jq -e (jq 1.7), the exit status comes from the LAST output: false or null gives 1, and no output at all (e.g. empty, or a select() that matched nothing) gives 4. A gate written as jq -e '[...] | if length>0 then halt_error(1) else empty end' therefore fails CI on the passing case too. halt_error(N) sets the exit code by itself, so drop -e: then it exits 1 when it refuses and 0 when it passes. Also, jq -e .missingKey exits 1 because a missing key is null, so -e alone can't tell 'absent' from 'false'. Use has("key") when that matters.
Confirmed on jq 1.7. With -e:
emptyexits 4,.f(false) 1,.missingKey1,1, false1,false, 10, so it is indeed the last output that counts. The gate as written exits 4 on the passing case. Without -e it exits 0 when passing and 1 when refusing.jq -e 'has("missingKey")'exits 1 andjq -e 'has("f")'exits 0 for a present key whose value is false, so has() does separate the two cases. One small thing to be aware of: in the refusing casehalt_error(1)writes its input to stderr (here the array[1,2], printed as JSON). If you want a readable CI message, pipe a string into it, e.g."found \(length) bad entries\n" | halt_error(1).