google-gemini / google-gemini/gemini-cli

read_file ignores the client's fs/read_text_file capability, unlike write_file and replace

Open
#29,108 0 comments 0 reactions 0 assignees View on GitHub
area/agent status/need-triage
Dominant language
TypeScript
Stars
107k
Forks
14.6k
Avg merge
2d 3h
Merged PRs (30d)
45

Description

### Summary

When a client connects over ACP advertising `fs: { readTextFile: true, writeTextFile: true }`, gemini-cli honors that capability for some file tools but not others. `write_file` and `replace` route their I/O through the client; `read_file` does its own disk I/O and never contacts the client at all.

The inconsistency between the CLI's own tools is the core of this request — `write_file` and `replace` already treat the client as the filesystem when it offers to be one, so `read_file` bypassing it looks like an oversight rather than a decision.

### Version

`@google/gemini-cli` **0.55.1**, run as `gemini --acp`.

### What I measured

A real `gemini --acp` session driven with scripted tool calls, with every ACP frame traced and the client's `fs/*` handlers instrumented. Only the model endpoint was stubbed (via `GOOGLE_GEMINI_BASE_URL`); the CLI's real tool-execution path ran everything downstream.

| tool | `fs/*` frames observed |
|---|---|
| `write_file` | `fs/read_text_file` (the pre-read) then `fs/write_text_file` ✅ |
| `replace` | `fs/read_text_file` before editing ✅ |
| `read_file` | **none** — reads the path directly off disk and returns the contents to the model ❌ |

For `read_file` the client's handler is never invoked and zero `fs/*` frames appear on the wire, while the model receives the true file contents.

### Why it matters to a client

A client advertising the fs capability is usually doing so because it *is* the filesystem:

- an editor with unsaved buffers, where disk contents are stale;
- a remote, virtual, or containerized workspace, where the CLI's local path is not the real file;
- a host applying its own access policy to what the agent may read.

In all three cases `read_file` silently returning on-disk bytes is wrong in a way the client cannot detect or correct, because it sees no request at all.

### Suggested fix

Route `read_file` through the client's `fs/read_text_file` when the capability is advertised, matching `write_file` and `replace`, and fall back to direct disk I/O otherwise.

### Two smaller notes from the same investigation

Both are peripheral to the request above, but may be useful context:

1. **`replace` re-reads the file with its own disk I/O during its correction pass**, even when the client's `fs/read_text_file` returned no content — so a client cannot fully back that tool either.

2. **A client-side read error cannot be reported as "file does not exist".** The bundled ACP SDK rejects `sendRequest` with a plain wire object `{code, message, data}` rather than an `Error`, and `normalizeFileSystemError` does `err instanceof Error ? err.message : String(err)`, which yields `"[object Object]"`. Because `write_file` pre-reads its target and detects a missing file by matching `ENOENT` in the message, that check can never fire — so an erroring `fs/read_text_file` breaks *every* write to a new file, and a client must return `{ content: '' }` for a missing file instead of an error.

### Context

Found while implementing ACP client filesystem handlers for a desktop host that uses them as its enforcement point for what an agent session may touch. Happy to supply the harness or a minimal reproduction if that would help.

Contributor guide

Open the contributing guide

Research direction

Start at the read_file tool implementation and compare its filesystem-capability handling with write_file and replace. Run the scripted ACP session with fs/read_text_file instrumented; done means read_file requests the client when readTextFile is advertised and falls back to direct disk I/O otherwise.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.