OpenHands / OpenHands/software-agent-sdk

[Bug]: system grep fallbacks misinterpret extended regular expressions

Open
#4,983 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug priority:medium ready-for-dev sdk tools
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Is there an existing issue for the same bug?
  • I have searched existing issues and this is not a duplicate.
Bug Description

When ripgrep is unavailable, GrepExecutor and the SDK terminal-compatibility fallback invoke system grep without -E. They therefore interpret patterns as basic regular expressions, although the same patterns work with the normal ripgrep backend. This silently loses valid matches and can also return the wrong files for escaped literal punctuation. Reproduced against current main at 9c3571a694547734002518bd94b3cb41a187f9b6.

Expected Behavior

The system grep fallbacks should support common extended regular expressions consistently with the ripgrep path:

  • Pattern ^(foo|bar)+[0-9]{2}$ should find matching.txt containing FooBAR12 (the tool searches case-insensitively).
  • Pattern ^foo\(bar\)\+$ should find matching.txt containing the literal text foo(bar)+, and not other.txt containing foobar.

Full equivalence between all regex dialects is outside this issue's scope.

Actual Behavior

The first pattern returns no matches. The second pattern returns other.txt instead of matching.txt. Neither result is reported as an error. Both production fallback command builders omit the extended-regex flag.

After setting up the base checkout and copying the reproduction script as described below, run this SDK reproduction from the repository root:

.venv/bin/python repro_grep_regex.py

It exits with status 1 and reports:

case=extended expected=[matching.txt] actual=[] is_error=false passed=false
case=escaped expected=[matching.txt] actual=[other.txt] is_error=false passed=false
Steps to Reproduce
  1. Check out main at 9c3571a694547734002518bd94b3cb41a187f9b6 and run make build to install the workspace packages.
  2. Copy the standalone reproduction linked under Minimal Code Sample to repro_grep_regex.py in that base checkout.
  3. Run .venv/bin/python repro_grep_regex.py. System grep must be installed. The script temporarily uses a PATH containing only system grep, so ripgrep is definitely absent; it restores PATH afterward. It uses real files and subprocesses, with no mocked production functions.
  4. Observe two failed cases and exit status 1. Adding -E to both system grep fallbacks makes both cases pass; regression tests also exercise the SDK terminal-compatibility path.
Acceptance Criteria
  • With ripgrep absent, GrepExecutor finds only matching.txt for both the extended-regex and escaped-literal cases above.
  • The SDK terminal-compatibility system grep command supports the same extended-regex syntax.
  • Regression tests execute actual system grep and cover the negative files, not only command construction.
  • Existing ripgrep behavior, case-insensitive search, result limits, and file filtering remain covered by the existing tests.
Installation Method

Source checkout; make build (uv workspace, editable local packages).

If you selected "Other", please specify

No response

SDK Version

1.47.0; main commit 9c3571a694547734002518bd94b3cb41a187f9b6 (fetched again before reporting).

Version Confirmation
  • I have confirmed this bug exists on the LATEST version of OpenHands SDK
Python Version

3.13.13

Model Name (if applicable)

Not applicable; this is a deterministic tool execution bug and requires no LLM.

Operating System

MacOS

Logs and Error Messages

Before the fix:

grep_available=true, rg=null
case=extended expected=[matching.txt] actual=[] is_error=false passed=false
case=escaped expected=[matching.txt] actual=[other.txt] is_error=false passed=false

After adding -E, both actual results are [matching.txt] and both pass.

Recorded evidence: baseline log, fixed log, validation commands and results. The focused grep and compatibility suite passed 85 tests after the fix.

Minimal Code Sample

The complete standalone reproduction is repro_grep_regex.py. Copy that script into the base checkout to reproduce the bug; the linked branch also contains the proposed production fix. The script creates both fixture files, hides ripgrep from PATH, invokes the public GrepAction/GrepExecutor entry point, checks both expected results, restores PATH, and exits nonzero when either case fails.

Screenshots and Additional Context

Affected command builders: openhands-tools/openhands/tools/grep/impl.py and openhands-sdk/openhands/sdk/agent/utils.py. The intended fix is to enable extended regular expressions using the portable grep -E option in both paths. A small tested patch and the evidence linked above are ready for a linked PR. No UI screenshots or model API calls are needed for this reproduction.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with openhands-tools/openhands/tools/grep/impl.py and openhands-sdk/openhands/sdk/agent/utils.py, then run .venv/bin/python repro_grep_regex.py from the repository root with ripgrep unavailable. Add regression coverage using actual system grep for both fixture cases and verify the focused grep and terminal-compatibility suites pass while existing ripgrep behavior remains covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, shell
Domain
cli, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.