Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[go-fan] Go Module Review: github.com/itchyny/gojqΒ #7670

Description

@github-actions

🐹 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:

  1. 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.
  2. 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)

  1. (Medium) Add *gojq.TypeError handling in runJqCode β€” small, self-contained, immediately improves filter debugging
  2. (Medium) Implement WithVariables in CompileToolResponseFilter + update config validation to match
  3. (Low) Add filter compilation cache (sync.Map) for identical filter expressions
  4. (Low) Move inferSchema to schema.go to decouple from gojq import
  5. (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 Β· β—·

  • expires on Jun 24, 2026, 8:56 AM UTC

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions