Skip to content

[duplicate-code] Duplicate Code Pattern: jq Filter Execution Logic in jqschema.go #5409

@github-actions

Description

@github-actions

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.goapplyJqSchema (lines ~180–220)
    • internal/middleware/jqschema.goapplyToolResponseFilter (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

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

  • Review duplication findings
  • Extract runJqCode helper in internal/middleware/jqschema.go
  • Update applyJqSchema to use runJqCode
  • Update applyToolResponseFilter to use runJqCode
  • Run make test to verify no regression
  • Verify error messages remain consistent with existing tests

Parent Issue

See parent analysis report: #5408
Related to #5408

Generated by Duplicate Code Detector · ● 2.1M ·

  • expires on May 17, 2026, 3:40 AM UTC

Metadata

Metadata

Assignees

Type

No type
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