OpenHands icon indicating copy to clipboard operation
OpenHands copied to clipboard

[Bug]: Line numbers are often included in `str_replace` commands

Open Tim-Pohlmann opened this issue 8 months ago • 14 comments

Is there an existing issue for the same bug? (If one exists, thumbs up or comment on the issue instead).

  • [x] I have checked the existing issues.

Describe the bug and reproduction steps

I'm not sure this qualifies as a bug. Might be an improvement request instead.

I have noticed a common pattern that leads to a failed command execution when OpenHands tries to edit a file via str_replace:

  1. It checks the file content via cat -n. The output includes line numbers (due to -n).
  2. It calls str_replace but it includes the line numbers in the argument strings. This results in the str_replace command not finding a match in the file and therefore not replace anything.

I'm running OpenHands via GitHub Actions, if that makes a difference.

I tried to mitigate the issue by adding some instructions in a .openhands_instructions file but with little to no effect.

OpenHands Installation

GitHub resolver

OpenHands Version

No response

Operating System

None

Logs, Errors, Screenshots, and Additional Context

No response

Tim-Pohlmann avatar Apr 16 '25 19:04 Tim-Pohlmann

I'm running OpenHands via GitHub Actions, if that makes a difference.

This doesn't make a difference. What LLM you are using, might matter, though, with what LLMs do you see this?

Cc: @ryanhoangt

enyst avatar Apr 16 '25 20:04 enyst

gemini/gemini-2.0-flash-lite

Tim-Pohlmann avatar Apr 17 '25 07:04 Tim-Pohlmann

  1. It checks the file content via cat -n. The output includes line numbers (due to -n).
  2. It calls str_replace but it includes the line numbers in the argument strings. This results in the str_replace command not finding a match in the file and therefore not replace anything.

This seems to me like a limitation in the LLM's tool use 🤔 ideally, after using cat -n, it should recognize that line numbers are just part of the display and shouldn't be included when passing file content to tools.

ryanhoangt avatar Apr 17 '25 08:04 ryanhoangt

I tried to mitigate the issue by adding some instructions in a .openhands_instructions file but with little to no effect.

I'm sorry, I see we haven't updated the resolver README.md on this, but we have deprecated the .openhands_instructions way of customizing instructions, in favor of a repo.md file in a particular location like this one is: https://github.com/All-Hands-AI/OpenHands/blob/main/.openhands/microagents/repo.md

repo.md

Could you please try that?

@openhands-agent Can you update the resolver README.md to point to the microagents' information about repo.md, instead of .openhands_instructions? The relevant link is the last I linked in this comment.

Ignore the rest of this discussion, just update the readme as appropriate.

enyst avatar Apr 17 '25 10:04 enyst

I can confirm I'm seeing the same thing with gemini pro 2.5 preview.

hamiltop avatar Apr 25 '25 14:04 hamiltop

Here's another related issue I'm seeing:

Command: str_replace
Old String: ```
\
        id: generate-token
        # Only run if App ID and Key are provided via secrets
        if: secrets.APP_ID && secrets.APP_PRIVATE_KEY
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

New String: ```
id: generate-token # Only run if App ID and Key are provided via secrets # Check env vars instead of secrets directly due to GitHub Actions limitations env: APP_ID_PRESENT: ${{ secrets.APP_ID != '' }} APP_PRIVATE_KEY_PRESENT: ${{ secrets.APP_PRIVATE_KEY != '' }} if: env.APP_ID_PRESENT == 'true' && env.APP_PRIVATE_KEY_PRESENT == 'true' uses: actions/create-github-app-token@v1 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }}


14:33:32 - OBSERVATION
[Agent Controller 2f6aa6765254499bac3fb73820efd5bb] ERROR:
No replacement was performed, old_str `\
        id: generate-token
        # Only run if App ID and Key are provided via secrets
        if: secrets.APP_ID && secrets.APP_PRIVATE_KEY
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}` did not appear verbatim in /workspace/OpenHands/.github/workflows/openhands-resolver.yml.
14:33:32 - openhands:INFO: agent_controller.py:965 - [Agent Controller 2f6aa6765254499bac3fb73820efd5bb] Cleared pending action after 0.03s: FileEditAction (id=131)
14:33:32 - openhands:INFO: agent_controller.py:411 - [Agent Controller 2f6aa6765254499bac3fb73820efd5bb] Stepping agent after event: FileEditObservation


==============
[Agent Controller 2f6aa6765254499bac3fb73820efd5bb] LEVEL 0 LOCAL STEP 41 GLOBAL STEP 41

14:33:36 - openhands:INFO: agent_controller.py:974 - [Agent Controller 2f6aa6765254499bac3fb73820efd5bb] Set pending action: FileReadAction (id=-1)
14:33:36 - ACTION
[Agent Controller 2f6aa6765254499bac3fb73820efd5bb] FileReadAction(path='/workspace/OpenHands/.github/workflows/openhands-resolver.yml', start=0, end=-1, thought="It seems the replacement failed again, likely due to subtle differences in whitespace or line endings in the `old_str`. Let's view the exact lines again and retry the replacement carefully.\n\n", action=<ActionType.READ: 'read'>, security_risk=None, impl_source=<FileReadSource.OH_ACI: 'oh_aci'>, view_range=[97, 103])
14:33:36 - OBSERVATION
[Agent Controller 2f6aa6765254499bac3fb73820efd5bb] [Read from /workspace/OpenHands/.github/workflows/openhands-resolver.yml is successful.]
Here's the result of running `cat -n` on /workspace/OpenHands/.github/workflows/openhands-resolver.yml:
    97	        id: generate-token
    98	        # Only run if App ID and Key are provided via secrets
    99	        if: secrets.APP_ID && secrets.APP_PRIVATE_KEY
   100	        uses: actions/create-github-app-token@v1
   101	        with:
   102	          app-id: ${{ secrets.APP_ID }}
   103	          private-key: ${{ secrets.APP_PRIVATE_KEY }}

14:33:36 - openhands:INFO: agent_controller.py:965 - [Agent Controller 2f6aa6765254499bac3fb73820efd5bb] Cleared pending action after 0.03s: FileReadAction (id=133)
14:33:36 - openhands:INFO: agent_controller.py:411 - [Agent Controller 2f6aa6765254499bac3fb73820efd5bb] Stepping agent after event: FileReadObservation


==============
[Agent Controller 2f6aa6765254499bac3fb73820efd5bb] LEVEL 0 LOCAL STEP 42 GLOBAL STEP 42

14:33:40 - openhands:INFO: agent_controller.py:974 - [Agent Controller 2f6aa6765254499bac3fb73820efd5bb] Set pending action: FileEditAction (id=-1)
14:33:40 - ACTION
[Agent Controller 2f6aa6765254499bac3fb73820efd5bb] **FileEditAction**
Path: [/workspace/OpenHands/.github/workflows/openhands-resolver.yml]
Thought: Okay, got the exact text. Let's try the replacement again.


Command: str_replace
Old String: ```
        id: generate-token
        # Only run if App ID and Key are provided via secrets
        if: secrets.APP_ID && secrets.APP_PRIVATE_KEY
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

New String: ``` id: generate-token # Only run if App ID and Key are provided via secrets # Check env vars instead of secrets directly due to GitHub Actions limitations env: APP_ID_PRESENT: ${{ secrets.APP_ID != '' }} APP_PRIVATE_KEY_PRESENT: ${{ secrets.APP_PRIVATE_KEY != '' }} if: env.APP_ID_PRESENT == 'true' && env.APP_PRIVATE_KEY_PRESENT == 'true' uses: actions/create-github-app-token@v1 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }}


14:33:40 - OBSERVATION
[Agent Controller 2f6aa6765254499bac3fb73820efd5bb] The file /workspace/OpenHands/.github/workflows/openhands-resolver.yml has been edited. Here's the result of running `cat -n` on a snippet of /workspace/OpenHands/.github/workflows/openhands-resolver.yml:
    94	          python-version: "3.12"
    95
    96	      - name: Generate GitHub App Token
    97	        id: generate-token
    98	        # Only run if App ID and Key are provided via secrets
    99	        # Check env vars instead of secrets directly due to GitHub Actions limitations
   100	        env:
   101	          APP_ID_PRESENT: ${{ secrets.APP_ID != '' }}
   102	          APP_PRIVATE_KEY_PRESENT: ${{ secrets.APP_PRIVATE_KEY != '' }}
   103	        if: env.APP_ID_PRESENT == 'true' && env.APP_PRIVATE_KEY_PRESENT == 'true'
   104	        uses: actions/create-github-app-token@v1
   105	        with:
   106	          app-id: ${{ secrets.APP_ID }}
   107	          private-key: ${{ secrets.APP_PRIVATE_KEY }}
   108
   109	      - name: Determine Auth Token
   110	        id: determine-auth-token
   111	        run: |
   112
Review the changes and make sure they are as expected. Edit the file again if necessary.

Again with gemini-2.5-pro-preview

It seems it's adding an initial newline? whatever the backslash is trying to do there.

hamiltop avatar Apr 25 '25 14:04 hamiltop

@openhands-agent-exp

I see we haven't updated the resolver README.md on microagents use, but we have deprecated the .openhands_instructions way of customizing instructions, in favor of a repo.md file in a particular location like this one is: https://github.com/All-Hands-AI/OpenHands/blob/main/.openhands/microagents/repo.md

repo.md

Can you update the resolver README.md to point to the microagents' information about repo.md, instead of .openhands_instructions? The relevant link is the last I linked in this comment.

You should explore to understand repo.md a bit. Use webread tool to see links. Alternatively, you can use GitHub API with the token in your env. Fix the resolver readme with this information.

You don't need to do anything else, I already took care of tests and all.

enyst avatar Apr 25 '25 14:04 enyst

OpenHands started fixing the issue! You can monitor the progress here.

openhands-agent avatar Apr 25 '25 14:04 openhands-agent

It seems it's adding an initial newline? whatever the backslash is trying to do there.

Oh! So the string to be replaced should NOT have had 3 backticks, nor the backlash/newline, right?

Edited to add:

@hamiltop This looks to me like an artefact of Gemini's way of doing edits. I think we should fix it, either by adapting to Gemini or rather, perhaps give it a new edit tool to do things.

enyst avatar Apr 25 '25 14:04 enyst

I know there's an experiment branch for Gemini, but it's got a lot of things. Can we extract the diffed fence editor from it? I can test that out if we think it's functional.

hamiltop avatar Apr 25 '25 14:04 hamiltop

FYI, Gemini corrected itself with this:

It seems like the leading \ might be the issue, or perhaps subtle whitespace differences. Let's try removing the \ and being very careful with the indentation and newlines.

And then succeeded in using the tool.

hamiltop avatar Apr 27 '25 15:04 hamiltop

This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Jun 05 '25 02:06 github-actions[bot]

Going to remove the stale here, but @enyst any idea if this was fixed?

mamoodi avatar Jun 05 '25 13:06 mamoodi

This was not fixed. I use a microagent locally, with this content, and Gemini 2.5 works better with it. @Tim-Pohlmann @hamiltop you may want to try it?

It can be any file with .md extension in .openhands/microagents, personally I name it local-gem.md:

# IMPORTANT NOTES

## IMPORTANT NOTES for the tool `str_replace_editor`

1. All commands that include line numbers are 1-indexed

2. Both `old_str` and `new_str` MUST start directly with the content of their respective first lines (including any indentation), WITHOUT prepending an escaped newline!

3. The `insert` command will insert a string after a specific line number in the file. Note that if you want to insert a string at the start of the file (line 1), you should use `insert_line=0`, and it will inserted *after* 0, so it will be on line 1.

4. The `view` command will show the file's contents in the indicated line number range, 1-indexed.

5. The line numbers followed by tab are added by the tool as an aid for you, they are not part of the file. So, do NOT add line numbers or the leading tab to the `old_str` or `new_str` parameters. Just the content from the file as is.

6. Do NOT use a leading `\n` or `\\n` in the `old_str` or `new_str` parameters. Just the content from the file as is.

If you get an error about old_str not found, first verify that these rules were followed.

I REPEAT: line numbers followed by tab are added by cat -n, they are not part of the file.

enyst avatar Jun 09 '25 18:06 enyst

Are there diff tools that acts differently, and give the LLM alternative options?

BradKML avatar Jun 30 '25 06:06 BradKML

This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Jul 31 '25 02:07 github-actions[bot]

This issue is stale because it has been open for 40 days with no activity. Remove the stale label or leave a comment, otherwise it will be closed in 10 days.

github-actions[bot] avatar Sep 10 '25 02:09 github-actions[bot]

This issue is stale because it has been open for 40 days with no activity. Remove the stale label or leave a comment, otherwise it will be closed in 10 days.

github-actions[bot] avatar Oct 21 '25 02:10 github-actions[bot]

This issue was automatically closed due to 50 days of inactivity. We do this to help keep the issues somewhat manageable and focus on active issues.

github-actions[bot] avatar Oct 31 '25 02:10 github-actions[bot]