HeyPuter/puter · error · HttpError
cannot_read_a_directory
cannot_read_a_directory
Error message
Cannot read a directory
What it means
Thrown by LegacyFSController.read after the target entry is resolved and read access is confirmed, when entry.isDir is true. The read endpoint streams file content; directories have no byte content, so the request is rejected with legacyCode 'cannot_read_a_directory'. Use readdir to list directory contents instead.
Source
Thrown at src/backend/controllers/fs/LegacyFSController.ts:1156
// Legacy v1 /read aliased `file` onto either path or uid depending on
// whether the value starts with `/`. resolveV1Selector does the same
// dispatch when handed a raw string.
const selector =
typeof query.file === 'string' && query.file.length > 0
? query.file
: query;
const entry = await resolveV1Selector(this.stores.fsEntry, selector);
await assertAccess(
this.services.acl,
this.services.fs,
actor,
entry.path,
'read',
);
if (entry.isDir) {
throw new HttpError(400, 'Cannot read a directory', {
legacyCode: 'cannot_read_a_directory',
});
}
const range =
typeof req.headers.range === 'string'
? req.headers.range
: undefined;
const download = await this.services.fs.readContent(entry, {
range,
});
// Force `application/octet-stream` on this endpoint for wire parity
// with v1. puter-js's `parseResponse` branches on Content-Type —
// `application/octet-stream` returns the raw Blob while other
// types wrap in `{success, result: Blob}`. Clients (including the
// GUI) expect the raw-Blob shape. Use `/fs/read` for type-aware
// streaming.View on GitHub (pinned to 908ec23eda)
Solutions
- Check entry.isDir before reading; if true, call readdir instead.
- In the UI, route folder selections to the listing flow and file selections to the read flow.
- Resolve/stat the entry first to branch on its type.
Example fix
// before
const blob = await puter.fs.read(entry);
// after
if (entry.is_dir) {
await puter.fs.readdir(entry);
} else {
const blob = await puter.fs.read(entry);
} Defensive patterns
Strategy: type-guard
Validate before calling
const entry = await stat(selector);
if (entry.is_dir) throw new Error('Cannot read a directory; use readdir'); Type guard
function isFileEntry(e) { return !!e && !e.is_dir; } Try / catch
null
Prevention
- Branch on is_dir before calling read; route directories to readdir.
- Resolve the entry first so you know its type.
- In the UI, prevent read actions on folder selections.
When it happens
Trigger: Calling /read (or puter.fs.read) with a path or uid that points to a directory; passing a folder uid where a file is expected.
Common situations: A generic 'open' handler that calls read regardless of entry type; a double-click on a folder inadvertently triggering a read; a stored uid that changed from file to folder.
Related errors
- dest_is_not_a_directory
- bad_request
- cannot_write_to_root
- subject_does_not_exist
- ${key} must be an integer between ${min} and ${max}
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/537b490020ca25b5.
Report an issue: GitHub.