Skip to content

Grep and Edit tools intermittently hang and never return on macOS #22139

Closed as not planned

Description

@MattPark

Description

The Grep and Edit tools intermittently hang after execution — the operation completes successfully (file is written, or ripgrep produces matches), but control never returns to the model. The agent gets stuck and the user must manually interrupt to continue.

Observed with both:

  • Grep tool — searches complete (verified by running rg via Bash with identical args and getting results), but the tool call never returns. Sometimes when interrupted, returns "No files found" even though matches exist.
  • Edit tool — edits are applied to disk successfully, but the tool call hangs on return. (Workaround: using sed via Bash instead.)

This is intermittent — not every call hangs. Unclear trigger, but it seems more likely to occur with larger files (~3,000+ lines).

Environment

  • OpenCode version: 1.4.3
  • OS: macOS (darwin)
  • Model: claude-opus-4.6 via github-copilot provider
  • Terminal: standard zsh

Steps to reproduce

  1. Open a session with a large Python file (~3,700 lines) in the workspace
  2. Use the Grep tool to search for a pattern that exists in the file, e.g.:
    Grep(pattern="def.*batch", include="audit.py")
    
  3. The tool hangs — no result is returned to the model. The user must interrupt.
  4. After interruption, the result is sometimes "No files found" (incorrect) or sometimes the correct matches.

The same pattern occurs with the Edit tool:

  1. Use Edit to replace a string in a large file
  2. The edit is applied on disk
  3. The tool call never returns

Expected behavior

Both Grep and Edit should return results to the model promptly after execution completes.

Workarounds

  • Grep: Use rg via the Bash tool instead
  • Edit: Use sed via the Bash tool instead

Notes

This may be related to the tool response streaming mechanism for large outputs, or a race condition in the tool completion signaling. The near-zero duration logged for some Grep calls (similar to #13353) suggests the tool may be short-circuiting or the response pipe may not be flushing correctly.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 12, 2026
  2. github-actions commented on Apr 12, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of or related to existing issues. Please check:

    If your issue is distinct (macOS-specific, large file size trigger, affects both Grep and Edit together), it's worth keeping open, but these may have related context or fixes.

  3. MattPark commented on Apr 12, 2026

    @MattPark
    Author

    Log evidence from --print-logs reproduction

    Reproduced with opencode --print-logs --log-level DEBUG (v1.4.3, macOS darwin, github-copilot/claude-opus-4.6).

    Full analysis posted on the related issue #17516: #17516 (comment)

    Summary

    Two sequential Grep calls in the same session. First succeeds (simple literal pattern), second hangs for 4m22s until user aborts (regex with alternation |). Both target the same ~3,700-line file.

    The log shows:

    • Permission is evaluated and granted for both calls
    • The successful call produces 4 message.part.updated bus events, then the session advances
    • The hanging call produces only 2 message.part.updated events, then 261 seconds of absolute silence — no error, no timeout, no crash — until the user aborts

    The grep tool's internal execution (ripgrep spawn, stdout reading) produces zero log entries at any level, making the hang invisible from the outside. The --log-level DEBUG flag produces zero DEBUG-level lines in the log, suggesting the tool internals aren't instrumented.

    This same behavior also affects the Edit and Write tools (confirmed across multiple sessions by the reporter).

  4. MattPark commented on Apr 12, 2026

    @MattPark
    Author

    Second reproduction — same pattern, same behavior

    Reproduced the hang a second time in the same session with the identical Grep call:

    Grep(pattern="def.*batch|batch_fix|_batch", include="audit.py")
    

    Log evidence (second reproduction)

    13:05:41 +363ms service=permission permission=grep pattern=def.*batch|batch_fix|_batch ... evaluated → allowed
    13:05:41 +3ms   service=bus type=message.part.updated publishing
    13:05:41 +6ms   service=bus type=message.part.updated publishing
                     ← 1,130,981ms (18 minutes 51 seconds) of ABSOLUTE SILENCE
    13:24:32         service=server method=POST path=/session/<id>/abort request  ← user aborted
    13:24:32         ERROR service=session.processor error=Aborted process
    

    Observations so far

    Call Pattern path param include Result
    1 def _run_batch_fix explicit dir audit.py Success
    2 def.*batch|batch_fix|_batch omitted audit.py Hung 4m22s
    3 def _run_batch_fix explicit dir audit.py Success
    4 def.*batch|batch_fix|_batch omitted audit.py Hung 18m51s

    The pattern with regex alternation (|) and no explicit path parameter hangs 100% of the time (2/2). The simple literal pattern with explicit path succeeds 100% (2/2).

    This may narrow it down: either the regex alternation in the ripgrep args causes a stdout pipe issue, or the omission of path triggers a different code path (perhaps searching from $HOME root which is a very large tree, and ripgrep output overwhelms the pipe buffer).

    In this session, the working directory is /Users/<user> (home directory). When path is omitted, the grep tool likely defaults to CWD = home, meaning ripgrep would recursively search the entire home directory for *.py files matching the pattern. That's a huge search space and could produce massive output — which might cause the stdout pipe to fill and deadlock if the consumer isn't draining it.

    The successful calls specified path as a specific subdirectory, limiting the search scope.

  5. MattPark commented on Apr 12, 2026

    @MattPark
    Author

    Confirmed: missing path parameter causes the hang

    Same pattern that hung twice (def.*batch|batch_fix|_batch, include=audit.py) returns instantly when an explicit path is provided:

    Call path Result
    Grep(pattern="def.*batch|batch_fix|_batch", include="audit.py") omitted (defaults to CWD = $HOME) Hung (2/2 attempts)
    Grep(pattern="def.*batch|batch_fix|_batch", include="audit.py", path="<project_dir>") explicit project subdir Instant (3 matches returned)

    Root cause hypothesis

    When path is omitted, the grep tool defaults to the current working directory. In this session, CWD is $HOME. Ripgrep recursively searches the entire home directory tree, which on this machine includes large project directories, virtual environments, node_modules, media files, etc.

    Even with --glob "audit.py" filtering, ripgrep must still traverse the entire directory tree to find matching filenames. On a large home directory, this traversal produces enough internal output (or takes long enough) to trigger a pipe buffer deadlock — ripgrep blocks waiting to write to stdout, while the OpenCode consumer blocks waiting for ripgrep to finish, and neither side makes progress.

    Suggested fix

    The grep tool should:

    1. Default to the project root (git root or workspace root) instead of CWD when path is omitted, similar to how IDEs scope searches
    2. Or implement a timeout on the ripgrep subprocess with a meaningful error message
    3. Or drain the stdout pipe asynchronously to prevent buffer deadlocks regardless of output volume
  6. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  7. github-actions commented on Jul 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions