windmill-labs/windmill · error
Cannot get content of directory
Error message
Cannot get content of directory
What it means
ZipFSElement builds a DynFS tree over a zip archive. Directory entries expose a getContentText() that always throws 'Cannot get content of directory', because a directory has no text content. Hitting it means caller code tried to read text from a node that is actually a directory.
Source
Thrown at cli/src/commands/sync/sync.ts:1813
},
};
// Yield the module lock file if present
if (mod.lock) {
const baseName = relPath.replace(/\.[^.]+$/, "");
yield {
isDirectory: false,
path: path.join(moduleFolderPath, baseName + ".lock"),
async *getChildren() {},
async getContentText() {
return mod.lock!;
},
};
}
}
},
async getContentText() {
throw new Error("Cannot get content of directory");
},
});
}
}
if (kind == "resource") {
const content = await f.async("text");
let parsed;
try {
parsed = JSON.parse(content);
} catch (error) {
log.error(`Failed to parse resource file content at path: ${p}`);
throw error;
}
const resourceType = parsed["resource_type"];
const formatExtension = resourceTypeToFormatExtension[resourceType];
const isFileset = resourceTypeToIsFileset[resourceType] ?? false;
if (View on GitHub (pinned to e474e8803c)
Solutions
- Check `entry.isDirectory` before calling getContentText() and handle directories by recursing into getChildren() instead
- If you expected a file, verify the path — you likely passed a folder path (e.g. a `.fileset/` or `__mod/` directory)
- Re-inspect the zip/export: if a genuine file is represented as a directory, fix the export
- Skip directory nodes in generic walkers
Example fix
// before
const text = await entry.getContentText();
// after
if (entry.isDirectory) {
for (const child of await entry.getChildren()) { /* recurse */ }
} else {
const text = await entry.getContentText();
} Defensive patterns
Strategy: type-guard
Validate before calling
if (entry.isDirectory) {
// handle children instead of reading text
} else {
const text = await entry.getContentText();
} Type guard
function isFileEntry(e: { isDirectory: boolean }): e is { isDirectory: false } {
return !e.isDirectory;
} Try / catch
try {
const text = await node.getContentText();
} catch (e) {
if (e.message === 'Cannot get content of directory') {
for (const child of await node.getChildren()) { /* recurse */ }
} else throw e;
} Prevention
- Always check isDirectory before getContentText()
- Walk trees via getChildren() and read only leaves
- Verify zip entry names — trailing '/' entries are directories
When it happens
Trigger: Calling getContentText() on a zip-tree element without checking isDirectory first — e.g. iterating sync entries and reading content from every node, or a misclassified zip entry (a folder-looking key treated as a file).
Common situations: Custom tooling walking the sync FS and blindly reading all entries; a zip produced with file entries whose names end in '/' and get classified as directories; scripts expecting a flat file layout but receiving a folder (e.g. a fileset or flow folder).
Related errors
- Cannot get content of folder
- Workspace folder not found, are you in the right directory?
- Content of raw app file ${filePath} is not a string
- Error reading dir: ${localP}, ${e}
- Found ${collisions.length} path(s) that differ only by lette
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/ba4e10b3786c0c2a.
Report an issue: GitHub.