d2lang/d2 · error
cannot read a directory
Error message
cannot read a directory
What it means
MemoryFileHandle.Read returns this error when the handle refers to a directory in the in-memory filesystem. Directories have no byte content to read, so the Read fails immediately instead of returning garbage or EOF.
Source
Thrown at lib/memfs/memfs.go:63
type MemoryFileHandle struct {
*MemoryFile
offset int
}
func (mfs *MemoryFS) Open(name string) (fs.File, error) {
file, ok := mfs.files[filepath.Clean(name)]
if !ok {
return nil, fs.ErrNotExist
}
return &MemoryFileHandle{MemoryFile: file}, nil
}
func (fh *MemoryFileHandle) Stat() (fs.FileInfo, error) { return fh.MemoryFile, nil }
func (fh *MemoryFileHandle) Read(b []byte) (int, error) {
if fh.isDir {
return 0, errors.New("cannot read a directory")
}
if fh.offset >= len(fh.content) {
return 0, io.EOF
}
n := copy(b, fh.content[fh.offset:])
fh.offset += n
return n, nil
}
func (fh *MemoryFileHandle) Close() error { return nil }
func (mf *MemoryFile) Stat() (fs.FileInfo, error) { return mf, nil }
func (mf *MemoryFile) Read(b []byte) (int, error) {
if mf.isDir {
return 0, errors.New("cannot read a directory")
}
copy(b, mf.content)
return len(mf.content), nilView on GitHub (pinned to 0d69dca6f5)
Solutions
- Check entry.IsDir() (or Stat on the handle) before calling Read and use fs.ReadDir/ReadDirAll for directories
- Fix the path to point to a file
- Add a type-switch or stat check in the caller to branch between file reads and directory listings
Example fix
// before
fh, _ := mfs.Open("assets/")
n, err := fh.Read(buf)
// after
fi, _ := fh.Stat()
if fi.IsDir() {
entries, err := mfs.ReadDir("assets/")
} else {
n, err := fh.Read(buf)
} Defensive patterns
Strategy: type-guard
Validate before calling
fi, err := mfs.Stat(path)
if err == nil && fi.IsDir() {
// use ReadDir instead of Read
} Type guard
func isFile(n fs.FileInfo) bool { return n != nil && !n.IsDir() } Try / catch
n, err := fh.Read(buf)
if err != nil && err.Error() == "cannot read a directory" {
entries, lerr := mfs.ReadDir(path)
// handle directory listing
} Prevention
- Stat entries and check IsDir() before reading
- Use fs.ReadDir/fs.WalkDir for traversal instead of blind reads
- Verify paths point to files in tests seeding memfs
When it happens
Trigger: Opening a directory entry from lib/memfs and calling Read on the resulting MemoryFileHandle; e.g. fh.Open on a path that is a directory, then fh.Read(buf).
Common situations: Walking a memfs tree and treating every entry as a file, using fs.ReadDir result incorrectly, or a path typo pointing at a directory instead of a file.
Related errors
- error copying %s: %v
- no color stops in gradient
- %s is not a supported format. Supported formats are: %s
- failed to fully compile (partial render written) %s: %w
- failed to compile %s: %w
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/34075fdf251a93d6.
Report an issue: GitHub.