Read tool: ENOENT on macOS filenames with U+00A0 NO-BREAK SPACE (Czech/Slovak Screenshot files)
Summary
On macOS, the Read tool returns File does not exist for files whose on-disk name contains a U+00A0 NO-BREAK SPACE (NBSP, UTF-8 \xc2\xa0), even though the file is present and ls / os.path.exists (with the correct bytes) confirm it. This bites every Czech/Slovak macOS user routinely, because the Czech-locale Screenshot tool in macOS embeds a NBSP between the preposition v (cs: "at") and the timestamp:
Snímek obrazovky 2026-05-15 v 13.05.05.png
^
U+00A0, not U+0020
The user can't visually tell NBSP from a regular space when typing the path in chat, so Read({file_path: "…v 13.05.05.png"}) resolves to a regular-space path (U+0020), which doesn't exist on APFS.
A naive guess is "APFS / Czech accents / NFD vs NFC" — but the filename is already NFC (í = U+00ED, not i+U+0301). The actual collision is solely U+00A0 vs U+0020.
Reproduction (macOS 14+, no special setup)
# 1. Create a file mimicking macOS Czech Screenshot output (NBSP between "v" and time)
python3 -c '
import os
fn = "Snímek obrazovky 2026-05-15 v 13.05.05.png"
open(fn, "wb").write(b"\x89PNG\r\n\x1a\n")
print("created:", repr(fn))
print("os.path.exists w/ NBSP:", os.path.exists(fn))
print("os.path.exists w/ SP :", os.path.exists(fn.replace(" ", " ")))
'
# Output:
# created: 'Snímek obrazovky 2026-05-15 v\xa013.05.05.png'
# os.path.exists w/ NBSP: True
# os.path.exists w/ SP : False
Now in Claude Code:
> Read this file: Snímek obrazovky 2026-05-15 v 13.05.05.png
→ File does not exist. (the IDE/CLI text input turns NBSP into a regular space when the user types or pastes the displayed name; the tool call then arrives with U+0020 and the literal lookup misses.)
Workaround that the user discovered:
cp Sn*13.05.05*.png /tmp/shot.png # shell glob expands against on-disk bytes
…then Read /tmp/shot.png works.
Why this matters
- Czech locale Screenshot (
Snímek obrazovky YYYY-MM-DD v HH.MM.SS.png) — NBSP betweenvand time. - Slovak locale Screenshot (
Snímka obrazovky YYYY-MM-DD o HH.MM.SS.png) — same pattern. - Other tools that ship NBSP-bearing filenames: Apple Numbers exports, some Office templates.
- Affects probably also
Edit,Write,Globwith literal patterns — anything that does a direct path lookup before fallback.
This is not a Unicode normalization (NFC/NFD) issue in the classic HFS+→APFS sense; modern APFS preserves whatever bytes you write. NFC/NFD round-trips don't change NBSP. The fix has to handle U+00A0 specifically, or use a compatibility-equivalent normalizer (NFKC) for fuzzy lookup.
Proposed fix
When the literal os.path.exists(path) is False, fall back to a single directory scan with NFKC-normalized basename comparison:
import os, unicodedata
def resolve_path(requested: str) -> str | None:
if os.path.exists(requested):
return requested
parent = os.path.dirname(requested) or "."
target = unicodedata.normalize("NFKC", os.path.basename(requested))
try:
candidates = [
f for f in os.listdir(parent)
if unicodedata.normalize("NFKC", f) == target
]
except OSError:
return None
if len(candidates) == 1:
return os.path.join(parent, candidates[0])
return None # ambiguous or genuinely missing
NFKC (compatibility decomposition + canonical composition) collapses U+00A0 → U+0020, fixes any NFC↔NFD mismatch on older HFS+ migrated trees, and stays a no-op for ASCII paths. Cost: one extra listdir only on the failure path, so the hot path is unchanged.
Alternative (cheaper, narrower): on ENOENT, retry once with path.replace(" ", " ") and once with path.replace(" ", " "). Less robust but trivially safe.
Environment
- macOS 14.x (Darwin 25.2.0)
- Claude Code CLI (latest)
- APFS volume
Out of scope for this issue
The @ autocomplete in the chat UI may or may not preserve NBSP — separate question. Even if it did, a user typing the path manually still can't produce a NBSP from a Czech keyboard layout without Option+Space, so the tool itself should be liberal in lookup.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗