πΉ Go Fan Report: gojq β Pure-Go jq Implementation
Module Overview
github.com/itchyny/gojq is a pure-Go jq implementation. It provides a full embeddable jq engine: parse expressions, compile to bytecode, run against Go-native values (no JSON round-trip), and consume results via an iterator. Zero external binary dependency.
Current Usage in gh-aw
- Files: 2 production files + 3 test/bench files
internal/middleware/jqschema.go β schema inference & tool response filtering
internal/config/validation.go β early compile-time validation of user jq filters
- Key APIs Used:
gojq.Parse, gojq.Compile, gojq.WithFunction, gojq.WithEnvironLoader, code.RunWithContext, iter.Next(), *gojq.HaltError
Architecture Pattern
Two-layer design:
- Schema layer (
walk_schema): Single jq call that delegates to a native Go function registered via gojq.WithFunction. All recursive tree-walking runs in compiled Go (inferSchema), not jq bytecode.
- Filter layer: User-supplied jq expressions compiled at handler-creation time, applied per-call via
RunWithContext.
Both share a runJqCode helper handling timeouts, HaltError, and error wrapping.
Well-implemented patterns β
: compile-once/run-many at init(), context timeout injection, native Go function registration for performance, $ENV access disabled for security, HaltError handling.
Research Findings
Module: github.com/itchyny/gojq v0.12.19 (current) | Repo: https://github.com/itchyny/gojq
Key gojq features available but not fully leveraged: gojq.WithVariables (injection-safe variable binding), *gojq.TypeError (typed error discrimination beyond *gojq.HaltError).
Improvement Opportunities
π Quick Wins
1. Handle *gojq.TypeError for better error messages
runJqCode catches *gojq.HaltError but falls back to a generic wrap for all other errors, including *gojq.TypeError. gojq's TypeError exposes .Value() and .Func() giving messages like type error (null) at .items[].id instead of the opaque current output.
// After HaltError check in runJqCode (line ~245):
var typeErr *gojq.TypeError
if errors.As(err, &typeErr) {
return nil, fmt.Errorf("%s type error: %w (check filter handles null/missing fields)", errPrefix, err)
}
2. Document iterator exhaustion contract
After iter.Next() returns, the iterator may hold state for additional values. For walk_schema this is always one result, but for user filters CheckMultipleResults is only checked when the flag is set. A brief comment makes the contract explicit for future contributors.
β¨ Feature Opportunities
3. Implement gojq.WithVariables for per-call context injection
The code already documents gojq.WithVariables in a comment as the correct approach for safe per-call metadata β but doesn't use it yet. Extending CompileToolResponseFilter to accept variable declarations would let users write context-aware filters like:
if $serverID == "github" then .items[:20] else . end
Values are bound at the Go level β no string interpolation, no injection risk.
// New variant (additive, non-breaking):
func CompileToolResponseFilterWithVars(filter string, varNames []string) (*gojq.Code, error)
4. Filter compilation cache
Identical filter expressions configured on multiple tools are compiled independently on each wrapToolHandler call. A sync.Map keyed by filter string would avoid redundant compilations in high-throughput gateways.
π Best Practice Alignment
5. Mirror WithVariables in config validation
validateToolResponseFilters in config/validation.go replicates runtime compile options so startup catches compile errors early. If WithVariables is added to the runtime (item 3), the validation path must declare the same variable names β otherwise valid filters referencing $toolName would be falsely rejected at startup.
π§ General Improvements
6. Decouple inferSchema from gojq
inferSchema is pure Go with no jq dependency β it only touches Go values. Moving it to a sibling schema.go would allow direct calls from non-jq paths and improve testability without importing gojq.
7. Use any alias instead of interface{}
The project targets Go 1.25. Internal functions like inferSchema, applyJqSchema, and runJqCode use interface{}. Updating to any is idiomatic for the Go version.
Module Summary
| Field |
Value |
| Module |
github.com/itchyny/gojq |
| Version |
v0.12.19 |
| Repository |
https://github.com/itchyny/gojq |
| Latest Release |
v0.12.19 (current, stable) |
| Last Reviewed |
2026-06-17 |
Key Features
- Pure-Go jq (no external binary)
WithFunction β native Go jq builtins
WithVariables β injection-safe variable binding
WithEnvironLoader β $ENV access control
RunWithContext β cancellable execution
*HaltError / *TypeError β typed error handling
References
Recommendations (Prioritized)
- (Medium) Add
*gojq.TypeError handling in runJqCode β small, self-contained, immediately improves filter debugging
- (Medium) Implement
WithVariables in CompileToolResponseFilter + update config validation to match
- (Low) Add filter compilation cache (
sync.Map) for identical filter expressions
- (Low) Move
inferSchema to schema.go to decouple from gojq import
- (Trivial) Update
interface{} β any in internal signatures
Next Steps
- Item 1 (TypeError) is a small, focused PR with immediate diagnostic value
- Items 2 + 5 should be implemented together to keep compile-time validation and runtime in sync
Generated by Go Fan πΉ Β· Run Β§27677040720
Generated by Go Fan Β· 550.4 AIC Β· β 34.4K Β· β·
πΉ Go Fan Report: gojq β Pure-Go jq Implementation
Module Overview
github.com/itchyny/gojqis a pure-Go jq implementation. It provides a full embeddable jq engine: parse expressions, compile to bytecode, run against Go-native values (no JSON round-trip), and consume results via an iterator. Zero external binary dependency.Current Usage in gh-aw
internal/middleware/jqschema.goβ schema inference & tool response filteringinternal/config/validation.goβ early compile-time validation of user jq filtersgojq.Parse,gojq.Compile,gojq.WithFunction,gojq.WithEnvironLoader,code.RunWithContext,iter.Next(),*gojq.HaltErrorArchitecture Pattern
Two-layer design:
walk_schema): Single jq call that delegates to a native Go function registered viagojq.WithFunction. All recursive tree-walking runs in compiled Go (inferSchema), not jq bytecode.RunWithContext.Both share a
runJqCodehelper handling timeouts,HaltError, and error wrapping.Well-implemented patterns β : compile-once/run-many at
init(), context timeout injection, native Go function registration for performance,$ENVaccess disabled for security,HaltErrorhandling.Research Findings
Module:
github.com/itchyny/gojq v0.12.19(current) | Repo: https://github.com/itchyny/gojqKey gojq features available but not fully leveraged:
gojq.WithVariables(injection-safe variable binding),*gojq.TypeError(typed error discrimination beyond*gojq.HaltError).Improvement Opportunities
π Quick Wins
1. Handle
*gojq.TypeErrorfor better error messagesrunJqCodecatches*gojq.HaltErrorbut falls back to a generic wrap for all other errors, including*gojq.TypeError. gojq'sTypeErrorexposes.Value()and.Func()giving messages liketype error (null) at .items[].idinstead of the opaque current output.2. Document iterator exhaustion contract
After
iter.Next()returns, the iterator may hold state for additional values. Forwalk_schemathis is always one result, but for user filtersCheckMultipleResultsis only checked when the flag is set. A brief comment makes the contract explicit for future contributors.β¨ Feature Opportunities
3. Implement
gojq.WithVariablesfor per-call context injectionThe code already documents
gojq.WithVariablesin a comment as the correct approach for safe per-call metadata β but doesn't use it yet. ExtendingCompileToolResponseFilterto accept variable declarations would let users write context-aware filters like:Values are bound at the Go level β no string interpolation, no injection risk.
4. Filter compilation cache
Identical filter expressions configured on multiple tools are compiled independently on each
wrapToolHandlercall. Async.Mapkeyed by filter string would avoid redundant compilations in high-throughput gateways.π Best Practice Alignment
5. Mirror
WithVariablesin config validationvalidateToolResponseFiltersinconfig/validation.goreplicates runtime compile options so startup catches compile errors early. IfWithVariablesis added to the runtime (item 3), the validation path must declare the same variable names β otherwise valid filters referencing$toolNamewould be falsely rejected at startup.π§ General Improvements
6. Decouple
inferSchemafrom gojqinferSchemais pure Go with no jq dependency β it only touches Go values. Moving it to a siblingschema.gowould allow direct calls from non-jq paths and improve testability without importing gojq.7. Use
anyalias instead ofinterface{}The project targets Go 1.25. Internal functions like
inferSchema,applyJqSchema, andrunJqCodeuseinterface{}. Updating toanyis idiomatic for the Go version.Module Summary
github.com/itchyny/gojqv0.12.19Key Features
WithFunctionβ native Go jq builtinsWithVariablesβ injection-safe variable bindingWithEnvironLoaderβ$ENVaccess controlRunWithContextβ cancellable execution*HaltError/*TypeErrorβ typed error handlingReferences
Recommendations (Prioritized)
*gojq.TypeErrorhandling inrunJqCodeβ small, self-contained, immediately improves filter debuggingWithVariablesinCompileToolResponseFilter+ update config validation to matchsync.Map) for identical filter expressionsinferSchematoschema.goto decouple from gojq importinterface{}βanyin internal signaturesNext Steps
Generated by Go Fan πΉ Β· Run Β§27677040720