Repository navigation
qj 0.3.0 matches jq 1.8.2, the patch release of 2026-06-20, where 0.2.x matched jq 1.8.1.
jq 1.8.2 is mostly security fixes, but many of them change what jq prints: error messages,
how deep it compares values, how it reads input. qj does what 1.8.2 does, byte for byte.
If you compare qj's output with jq's, compare it with jq 1.8.2.
What changes, as in jq 1.8.2
- Error messages. Indexing errors always name the key:
Cannot index array with string ("a"), where 1.8.1 saidCannot index array with string "a"(and nothing for keys of
other types). Values quoted in errors are cut at 30 bytes instead of 15, keeping their
closing quote or bracket:string ("héllo wörld 😀") number required. - Depth limits. Comparing (
==,<,sort,unique,group_by, ...),contains,
object*and paths (getpath,setpath,delpaths) stop past 10,000 levels with an
error:Equality check too deep,Comparison too deep,Containment check too deep,
Object merge too deep,Path too deep. Values nested deeper than 256 levels now print
in full, up to 10,000. - Builtins.
tonumberandtobooleanreject strings with a NUL byte.@uriddecodes a whole string and then checks it is valid UTF-8._strindicesraises a type error for non-strings, and so no longer forces records to
run one at a time.rtrimstr("")returns its input (it returned"").from_entriesuses//rather than//=.
- Input.
- A NUL byte in JSON input no longer cuts the line short.
- With
-R, a multi-byte character that straddles jq's 4 KB read boundary stays whole. -frejects a program file with a NUL byte (program file contains NUL bytes, exit 2).
- Modules.
- A module that imports itself, directly or through others, is an error (
circular import of <path>, exit 3); in compat mode jq 1.8.1's crash is gone. - A function can have at most 4,095 local definitions and parameters.
- A module that imports itself, directly or through others, is an error (
--run-teststakes several test files, and--skip/--takewithout a count is an
error (it crashed).- The usage hint after an option error says "online docs at" with one space.
jq 1.8.2's own crashes
jq 1.8.2 has a new crash: in a chain of three or more modules, its linker frees
definitions that are still called, and compiling the call aborts on an assert()
(import "m0" as m; m::f, where m0 imports m1 and m1 imports m2). It happens the same
way in every run, but how it comes out depends on the build: each of macOS arm64, macOS
x86-64, glibc Linux, musl and Windows aborts in its own way. As with jq's other crashes,
qj returns the answer by default, and QJ_JQ_COMPAT=1 reproduces each measured platform's
abort. So does compat mode for the aborts after some of the new 4,095-closure errors.
The crashes and hangs of jq 1.8.1 that 1.8.2 fixed are gone from compat mode too: the
import cycle, delpaths with a nan path element, the stack overflow freeing a deeply
nested value, and jv_dels' double free.
Compat mode's model of jq's C stack was re-measured against the 1.8.2 release binaries on
macOS and Linux. With the 10,000-level limits, jq's recursions over values can only
overflow a stack of about 1.8 MB or less; there, compat mode still dies where jq does, a
few levels early and never late.
Conformance
jq_diff, against jq 1.8.2's release binaries:
| Cases | qj as it is | QJ_JQ_COMPAT=1 |
|
|---|---|---|---|
| macOS arm64 | 40,347 | 40,340 | 40,347 (100%) |
| macOS x86-64 | 40,347 | 40,340 | 40,347 (100%) |
| Linux x86-64 | 40,372 | 40,365 | 40,372 (100%) |
| Linux aarch64 | 40,372 | 40,365 | 40,372 (100%) |
The 7 cases qj as it is doesn't match are its own help and version text. On FreeBSD and
NetBSD, against jq 1.8.2 built there, the same 7 are all that differ; on musl, also the
order of large outputs when stdout and stderr share a file, and one module chain whose
crash comes out differently there. On Windows, qj matches jq-windows-amd64.exe 1.8.2
except the known differences in
docs/LIMITATIONS.md.
Details in docs/COMPATIBILITY.md.
Prebuilt binaries: macOS (arm64, x86_64), Linux (x86_64, aarch64) and Windows (x86_64), or
cargo install qj.
Full changelog: v0.2.1...v0.3.0