CircuitPython version and board name
Adafruit CircuitPython 10.2.0 on 2026-04-22; Adafruit Feather ESP32 V2 with ESP32
Board ID: adafruit_feather_esp32_v2
Code/REPL
`boot.py` mounts a FAT32 microSD card at `/sd` (Adalogger FeatherWing, SPI, CS on `board.D33`). The reproducer then writes a file whose name begins with a `.`:
import os
# Confirm /sd is mounted and writable
with open("/sd/normal_file.txt", "w") as f:
f.write("hello\n")
f.flush()
# Write a leading-dot filename to the same volume
with open("/sd/.hidden_file.txt", "w") as f:
f.write("hello dot\n")
f.flush()
# Check what actually landed on disk
print("listdir('/sd') =", os.listdir("/sd"))
# Attempt to read both back
for name in ("normal_file.txt", ".hidden_file.txt"):
try:
with open("/sd/" + name) as f:
print(f" {name}: {f.read()!r}")
except OSError as exc:
print(f" {name}: OSError {exc}")
Behavior
listdir('/sd') = ['normal_file.txt'] # .hidden_file.txt is absent
normal_file.txt: 'hello\n'
.hidden_file.txt: OSError [Errno 2] No such file/directory
No exception is raised during the open(..., "w") or f.write() or f.flush() calls for the dot-file. The code appears to succeed, but the file is never written to the FAT directory. It does not appear in os.listdir() and cannot be read back.
Description
Expected behavior
Either:
- The write succeeds and the file appears in
os.listdir("/sd") (correct POSIX behaviour — FAT32 has no concept of hidden dot-files; it uses a separate attribute flag), or
- An
OSError is raised at open("/sd/.hidden_file.txt", "w") so the caller knows the write failed.
Silent success with no data written is never acceptable.
Diagnosis
FAT LFN (Long File Name) entries should preserve leading dots — .hidden_file.txt is a valid LFN. The FatFS library itself handles LFN correctly. The bug is likely in CircuitPython's VfsFat Python binding layer or in how it converts a Python filename string into an FatFS path before calling f_open. The leading dot may be stripped, normalised to an 8.3 short name that conflicts, or rejected by a filter that silently returns FR_OK instead of an error.
Workaround
Use a filename without a leading dot:
# BROKEN — silent data loss
with open("/sd/.sd_capacity", "w") as f:
f.write("15931539456")
# WORKS
with open("/sd/sd_capacity.txt", "w") as f:
f.write("15931539456")
Impact
Silent data loss. Any code that writes configuration, cache, or sentinel files with dot-prefixed names (a common Unix convention) will believe the write succeeded while the file is never actually stored. This is particularly dangerous in boot.py — the one place where writes happen before code.py gets control — because there is no feedback path to the user if the write silently fails.
Additional information
Hardware
Related
- Adafruit CircuitPython issue #XXXX —
os.statvfs("/sd") returns root VFS stats (same VfsFat layer, separate bug)
CircuitPython version and board name
Code/REPL
Behavior
No exception is raised during the
open(..., "w")orf.write()orf.flush()calls for the dot-file. The code appears to succeed, but the file is never written to the FAT directory. It does not appear inos.listdir()and cannot be read back.Description
Expected behavior
Either:
os.listdir("/sd")(correct POSIX behaviour — FAT32 has no concept of hidden dot-files; it uses a separate attribute flag), orOSErroris raised atopen("/sd/.hidden_file.txt", "w")so the caller knows the write failed.Silent success with no data written is never acceptable.
Diagnosis
FAT LFN (Long File Name) entries should preserve leading dots —
.hidden_file.txtis a valid LFN. The FatFS library itself handles LFN correctly. The bug is likely in CircuitPython's VfsFat Python binding layer or in how it converts a Python filename string into an FatFS path before callingf_open. The leading dot may be stripped, normalised to an 8.3 short name that conflicts, or rejected by a filter that silently returnsFR_OKinstead of an error.Workaround
Use a filename without a leading dot:
Impact
Silent data loss. Any code that writes configuration, cache, or sentinel files with dot-prefixed names (a common Unix convention) will believe the write succeeded while the file is never actually stored. This is particularly dangerous in
boot.py— the one place where writes happen beforecode.pygets control — because there is no feedback path to the user if the write silently fails.Additional information
Hardware
board.D33)Related
os.statvfs("/sd")returns root VFS stats (same VfsFat layer, separate bug)