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.

Solutions

  1. Check entry.IsDir() (or Stat on the handle) before calling Read and use fs.ReadDir/ReadDirAll for directories
  2. Fix the path to point to a file
  3. 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

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


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/34075fdf251a93d6. Report an issue: GitHub.

Appendix: 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), nil

View on GitHub (pinned to 0d69dca6f5)