zylon-ai/private-gpt · error · RuntimeError
Unable to replace text
Error message
Unable to replace text
What it means
RuntimeError from the str_replace tool: the session's str_replace(path, old_str, new_str) returned success=False. The raised message is result.error if the session provided detail, else 'Unable to replace text'. Typical session-side causes: file not found, old_str not present or appearing multiple times (ambiguous replacement).
Source
Thrown at private_gpt/components/tools/builders/text_editor_tool_builder.py:142
),
)
async def build_str_replace_tool(
self,
config: CodeExecutionSessionConfig,
name: str = TEXT_EDITOR_STR_REPLACE_TOOL_NAME,
type: str = TEXT_EDITOR_STR_REPLACE_TOOL_NAME + "_v1",
description: str = TEXT_EDITOR_STR_REPLACE_TOOL_FN.metadata.description,
) -> ToolSpec:
async def str_replace(
path: str,
old_str: str,
new_str: str,
) -> list[ResultContentBlockType]:
session = await self._session(config)
result = await session.str_replace(path, old_str, new_str)
if not result.success:
raise RuntimeError(result.error or "Unable to replace text")
output = _truncated(
result.output, self._settings.code_execution.max_output_bytes
)
return [TextEditorCodeExecutionStrReplaceResultBlock(lines=[output])]
return ToolSpec.from_defaults(
name=name,
type=type,
runtime="server",
event_adapter=TextEditorCodeExecutionEventAdapter,
description=description,
async_fn=str_replace,
requirements=[ToolRequirements.SANDBOX],
execution_metadata=build_rebuild_metadata(
rebuild_text_editor_str_replace_tool,
{
"config": config,
"name": name,View on GitHub (pinned to 4a030776a3)
Solutions
- Read the current file content first (view tool) and copy old_str verbatim, preserving whitespace.
- If the error message is generic, capture result.error to get the session's reason (not found vs no match vs multiple matches).
- Recreate the session if it expired, then retry; ensure the path is the same one used to create/read the file.
Defensive patterns
Strategy: try-catch
Validate before calling
# best pre-check: read the file first and confirm old_str occurs exactly once blocks = await view(path=path) content = blocks[0].content if blocks else "" assert content.count(old_str) == 1, "old_str must match exactly once before str_replace"
Try / catch
try:
blocks = await str_replace(path=path, old_str=old_str, new_str=new_str)
except RuntimeError as e:
detail = str(e) if str(e) != "Unable to replace text" else "replace failed without detail"
if "not found" in detail or "match" in detail:
# re-read the file and regenerate old_str from current content
pass
raise Prevention
- Always view the file immediately before str_replace and copy old_str verbatim (exact whitespace).
- Ensure old_str is unique in the file; include surrounding context lines if it is ambiguous.
- Keep view and replace in the same session turn to avoid content drift.
When it happens
Trigger: Calling str_replace on a path that does not exist in the sandbox, with an old_str that does not exactly match the file content (whitespace/indentation), or an old_str that occurs more than once and the provider requires uniqueness.
Common situations: LLM-hallucinated snippet that does not match the file; tabs-vs-spaces mismatch after a view/read of the file; editing a file in an expired or restarted sandbox session; race with another tool having rewritten the file.
Related errors
- Unable to view file
- Unable to create file
- Path '{canonical_path}' does not match any session mount.
- Code execution failed: {message}
- Execution failed: {result.error or 'Unknown error'}
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/109d2d4082291a47.
Report an issue: GitHub.