windmill-labs/windmill · error

Cannot get content of folder

Error message

Cannot get content of folder

What it means

The internal folder node of the zip-based sync FS (the root '.' entry built by _internal_folder) also stubs getContentText() with 'Cannot get content of folder'. It is thrown when someone tries to read the text of the virtual root or any synthesized folder node rather than a real archived file.

Source

Thrown at cli/src/commands/sync/sync.ts:1901

      isDirectory: true,
      path: p,
      async *getChildren(): AsyncIterable<DynFSElement> {
        for (const filename in zip.files) {
          const file = zip.files[filename];
          const totalPath = path.join(p, filename);
          if (file.dir) {
            const e = zip.folder(file.name)!;
            yield _internal_folder(totalPath, e);
          } else {
            const fs = await _internal_file(totalPath, file);
            for (const f of fs) {
              yield f;
            }
          }
        }
      },
      async getContentText(): Promise<string> {
        throw new Error("Cannot get content of folder");
      },
    };
  }
  return _internal_folder("." + SEP, zip);
}

/**
 * Directories no walk over a workspace ever descends, whatever the sync scope:
 * dependency trees, and the dot-directories that hold tooling state and
 * fixtures. Exported because a second walk that disagrees with this one reads
 * files sync will never see, and draws conclusions from them.
 */
export function isNeverWalkedDir(dirName: string | undefined): boolean {
  return (
    dirName === "node_modules" || (dirName !== undefined && dirName.startsWith("."))
  );
}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Start from getChildren() on the root and only call getContentText() on leaf file entries
  2. Add an isDirectory check before any content read
  3. If you need a text dump of the archive, iterate files individually rather than reading folders

Example fix

// before
const all = await root.getContentText();
// after
for (const child of await root.getChildren()) {
  if (!child.isDirectory) console.log(child.path, await child.getContentText());
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (node.path === '.' + SEP || node.isDirectory) {
  throw new Error('use getChildren() on folders');
}
const text = await node.getContentText();

Type guard

function isRootFolder(e: DynFSElement): boolean {
  return e.isDirectory && e.path === '.' + path.sep;
}

Try / catch

try {
  const text = await node.getContentText();
} catch (e) {
  if (e.message === 'Cannot get content of folder') {
    // it's the root/virtual folder — iterate children instead
  } else throw e;
}

Prevention

When it happens

Trigger: Calling getContentText() on the root element returned by ZipFSElement/_internal_folder, or on any synthesized folder while walking the zip tree; treating the whole zip tree as a single text blob.

Common situations: Scripts that dump every entry's content starting from the root; confusing the folder abstraction (getChildren) with file reading; debugging code that assumed the root had content.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/edd66884ba8ed297. Report an issue: GitHub.