claude-code - 💡(How to fix) Fix Read tool: ENOENT on macOS filenames with U+00A0 NO-BREAK SPACE (Czech/Slovak Screenshot files)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

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.

Error Message

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

Root Cause

  • Czech locale Screenshot (Snímek obrazovky YYYY-MM-DD v HH.MM.SS.png) — NBSP between v and 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, Glob with 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.

Fix Action

Fix / Workaround

Workaround that the user discovered:

Code Example

Snímek obrazovky 2026-05-15 v 13.05.05.png
                              ^
                              U+00A0, not U+0020

---

# 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

---

> Read this file: Snímek obrazovky 2026-05-15 v 13.05.05.png

---

cp Sn*13.05.05*.png /tmp/shot.png   # shell glob expands against on-disk bytes

---

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
RAW_BUFFERClick to expand / collapse

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 between v and 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, Glob with 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.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING