What happened?
When gemini runs as an ACP agent and the host returns a file-system error from a delegated readTextFile call, AcpFileSystemService.normalizeFileSystemError (packages/cli/src/acp/acpFileSystemService.ts) decides whether the error means "file not found" by substring-matching the human-readable message against four exact phrases:
errorMessage.includes('Resource not found') ||
errorMessage.includes('ENOENT') ||
errorMessage.includes('does not exist') ||
errorMessage.includes('No such file')
The structured JSON-RPC code field on the error response (-32002 = RESOURCE_NOT_FOUND per the ACP spec) is ignored. Common phrasings produced by ACP hosts that don't match any of those four substrings — for example not_found: file not found, no such file or directory, or file_not_found — are classified as generic file-system errors instead of ENOENT.
The user-visible symptom: write_file on a new path internally calls readTextFile first to check whether the file exists. When the read returns a not-found error that fails the substring check, the write tool sees a generic FS error rather than the expected ENOENT, treats the path as "exists but unreadable", and either retries or reports an opaque failure. From outside, the agent reasoning loop appears to hallucinate success after several retries because no write actually lands.
This was discovered wiring gemini --experimental-acp into a custom ACP host. When the host's read errors carried message not_found: file not found and code -32002, every write_file call on a non-existent path silently failed (multi-minute hangs, watchdog restarts). Reformatting the host's error message to include the literal substring ENOENT made every write start succeeding immediately — confirming the substring matcher is the load-bearing path.
#14190 attempted a similar fix earlier but was closed un-merged on 2026-01-24. #17588 was closed on the assumption that #14190 had landed, so the symptom is still live in 0.40.1.
What did you expect to happen?
normalizeFileSystemError should:
- Inspect the JSON-RPC
code field first and return ENOENT for -32002 (RESOURCE_NOT_FOUND) regardless of message phrasing.
- Recognize broader human-readable not-found phrasings (
no such file, file_not_found, not_found, cannot find) so that hosts using non-canonical wording still produce ENOENT downstream.
Client information
Client Information
gemini --version: 0.40.1
Platform: darwin arm64 (Darwin 25.4.0)
Mode: --experimental-acp
(Discovered while running gemini --experimental-acp against a custom ACP host implementing fs.readTextFile / fs.writeTextFile delegation. The bug reproduces against any host whose not-found error message doesn't contain one of the four hardcoded substrings.)
Login information
Google account.
Anything else we need to know?
What happened?
When
geminiruns as an ACP agent and the host returns a file-system error from a delegatedreadTextFilecall,AcpFileSystemService.normalizeFileSystemError(packages/cli/src/acp/acpFileSystemService.ts) decides whether the error means "file not found" by substring-matching the human-readable message against four exact phrases:The structured JSON-RPC
codefield on the error response (-32002=RESOURCE_NOT_FOUNDper the ACP spec) is ignored. Common phrasings produced by ACP hosts that don't match any of those four substrings — for examplenot_found: file not found,no such file or directory, orfile_not_found— are classified as generic file-system errors instead of ENOENT.The user-visible symptom:
write_fileon a new path internally callsreadTextFilefirst to check whether the file exists. When the read returns a not-found error that fails the substring check, the write tool sees a generic FS error rather than the expected ENOENT, treats the path as "exists but unreadable", and either retries or reports an opaque failure. From outside, the agent reasoning loop appears to hallucinate success after several retries because no write actually lands.This was discovered wiring
gemini --experimental-acpinto a custom ACP host. When the host's read errors carried messagenot_found: file not foundand code-32002, everywrite_filecall on a non-existent path silently failed (multi-minute hangs, watchdog restarts). Reformatting the host's error message to include the literal substringENOENTmade every write start succeeding immediately — confirming the substring matcher is the load-bearing path.#14190 attempted a similar fix earlier but was closed un-merged on 2026-01-24. #17588 was closed on the assumption that #14190 had landed, so the symptom is still live in
0.40.1.What did you expect to happen?
normalizeFileSystemErrorshould:codefield first and return ENOENT for-32002(RESOURCE_NOT_FOUND) regardless of message phrasing.no such file,file_not_found,not_found,cannot find) so that hosts using non-canonical wording still produce ENOENT downstream.Client information
Client Information
(Discovered while running
gemini --experimental-acpagainst a custom ACP host implementingfs.readTextFile/fs.writeTextFiledelegation. The bug reproduces against any host whose not-found error message doesn't contain one of the four hardcoded substrings.)Login information
Google account.
Anything else we need to know?
write_filecall to a non-existent path when the host's not-found error doesn't match one of the four hardcoded substrings.error.code === -32002first and expanding the substring set.