Part of duplicate code analysis: #5408
Summary
The functions applyJqSchema and applyToolResponseFilter in internal/middleware/jqschema.go share nearly identical logic for running a gojq filter and handling its output. Approximately 25 lines of error-handling code are structurally duplicated between the two functions.
Duplication Details
Pattern: gojq filter execution + error-handling block
- Severity: Medium
- Occurrences: 2 instances in the same file
- Locations:
internal/middleware/jqschema.go — applyJqSchema (lines ~180–220)
internal/middleware/jqschema.go — applyToolResponseFilter (lines ~237–270)
Duplicated code sample (applyJqSchema):
if _, hasDeadline := ctx.Deadline(); !hasDeadline {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, DefaultJqTimeout)
defer cancel()
}
iter := jqSchemaCode.RunWithContext(ctx, jsonData)
v, ok := iter.Next()
if !ok {
return nil, fmt.Errorf("jq schema filter returned no results")
}
if err, ok := v.(error); ok {
if ctx.Err() != nil {
return nil, fmt.Errorf("jq query execution failed: %w", ctx.Err())
}
if haltErr, ok := err.(*gojq.HaltError); ok {
if haltErr.Value() == nil {
return nil, fmt.Errorf("jq schema filter halted cleanly with no output")
}
return nil, fmt.Errorf("jq schema filter halted with error (exit code %d): %w", haltErr.ExitCode(), err)
}
return nil, fmt.Errorf("jq schema filter error: %w", err)
}
Nearly identical block in applyToolResponseFilter (lines ~237–265) — differs only in error-message prefix strings.
Impact Analysis
- Maintainability: If the error-handling strategy changes (e.g., new gojq error types, different timeout behaviour), both functions must be updated independently — easy to miss one.
- Bug Risk: Low — both copies currently behave correctly, but inconsistency could creep in during future edits.
- Code Bloat: Minor (~25 extra lines).
Refactoring Recommendations
- Extract a private helper
runJqCode
// runJqCode applies a pre-compiled gojq code object to jsonData.
// It enforces DefaultJqTimeout when the context has no deadline.
// errPrefix is used to namespace returned error messages (e.g. "jq schema filter").
func runJqCode(ctx context.Context, code *gojq.Code, jsonData interface{}, errPrefix string) (interface{}, error) {
if _, hasDeadline := ctx.Deadline(); !hasDeadline {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, DefaultJqTimeout)
defer cancel()
}
iter := code.RunWithContext(ctx, jsonData)
v, ok := iter.Next()
if !ok {
return nil, fmt.Errorf("%s returned no results", errPrefix)
}
if err, ok := v.(error); ok {
if ctx.Err() != nil {
return nil, fmt.Errorf("%s execution failed: %w", errPrefix, ctx.Err())
}
if haltErr, ok := err.(*gojq.HaltError); ok {
if haltErr.Value() == nil {
return nil, fmt.Errorf("%s halted cleanly with no output", errPrefix)
}
return nil, fmt.Errorf("%s halted with error (exit code %d): %w", errPrefix, haltErr.ExitCode(), err)
}
return nil, fmt.Errorf("%s error: %w", errPrefix, err)
}
return v, nil
}
- Both
applyJqSchema and applyToolResponseFilter delegate to runJqCode.
applyToolResponseFilter would call runJqCode then add its extra multiple-result check.
- Estimated effort: ~30 minutes.
Implementation Checklist
Parent Issue
See parent analysis report: #5408
Related to #5408
Generated by Duplicate Code Detector · ● 2.1M · ◷
Part of duplicate code analysis: #5408
Summary
The functions
applyJqSchemaandapplyToolResponseFilterininternal/middleware/jqschema.goshare nearly identical logic for running a gojq filter and handling its output. Approximately 25 lines of error-handling code are structurally duplicated between the two functions.Duplication Details
Pattern: gojq filter execution + error-handling block
internal/middleware/jqschema.go—applyJqSchema(lines ~180–220)internal/middleware/jqschema.go—applyToolResponseFilter(lines ~237–270)Duplicated code sample (applyJqSchema):
Nearly identical block in
applyToolResponseFilter(lines ~237–265) — differs only in error-message prefix strings.Impact Analysis
Refactoring Recommendations
runJqCodeapplyJqSchemaandapplyToolResponseFilterdelegate torunJqCode.applyToolResponseFilterwould callrunJqCodethen add its extra multiple-result check.Implementation Checklist
runJqCodehelper ininternal/middleware/jqschema.goapplyJqSchemato userunJqCodeapplyToolResponseFilterto userunJqCodemake testto verify no regressionParent Issue
See parent analysis report: #5408
Related to #5408