[Bug]: Line numbers are often included in `str_replace` commands
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:
- It checks the file content via
cat -n. The output includes line numbers (due to-n). - It calls
str_replacebut it includes the line numbers in the argument strings. This results in thestr_replacecommand 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
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
gemini/gemini-2.0-flash-lite
- It checks the file content via
cat -n. The output includes line numbers (due to-n).- It calls
str_replacebut it includes the line numbers in the argument strings. This results in thestr_replacecommand 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.
I tried to mitigate the issue by adding some instructions in a
.openhands_instructionsfile 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
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.
I can confirm I'm seeing the same thing with gemini pro 2.5 preview.
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.
@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
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.
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.
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.
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.
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.
Going to remove the stale here, but @enyst any idea if this was fixed?
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.
Are there diff tools that acts differently, and give the LLM alternative options?
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.
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.
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.
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.