Hmbown/CodeWhale · error · Error
Release asset directory must be flat; found: ${nonFiles.map(
Error message
Release asset directory must be flat; found: ${nonFiles.map((entry) => entry.name).join(", ")} What it means
verifyAssetDirectory() requires the release asset directory to contain only regular files at the top level; any subdirectory, symlink, or other non-file entry triggers this error with the offending names listed. Release assets are flat by design and symlinks would defeat content hashing and reproducible packaging.
Source
Thrown at scripts/release/assemble-release-assets.js:85
const manifestPath = path.join(directory, manifestName);
const checksums = parseChecksumManifest(
await fs.readFile(manifestPath, "utf8"),
manifestName,
);
assertExactNames([...checksums.keys()], expectedNames, manifestName);
for (const name of expectedNames) {
const actual = await sha256(path.join(directory, name));
if (checksums.get(name) !== actual) {
throw new Error(`${manifestName} checksum mismatch for ${name}`);
}
}
}
async function verifyAssetDirectory(directory) {
const entries = await fs.readdir(directory, { withFileTypes: true });
const nonFiles = entries.filter((entry) => !entry.isFile());
if (nonFiles.length > 0) {
throw new Error(
`Release asset directory must be flat; found: ${nonFiles.map((entry) => entry.name).join(", ")}`,
);
}
const expected = allReleaseAssetNames();
assertExactNames(entries.map((entry) => entry.name), expected, "Release asset directory");
await assertManifest(directory, CHECKSUM_MANIFEST, checksummedReleaseAssetNames());
await assertManifest(directory, BUNDLE_CHECKSUM_MANIFEST, BUNDLE_ASSET_NAMES);
console.log(`Verified ${expected.length} release assets in ${directory}`);
}
function windowsLauncherContents() {
return [
"@echo off",
"where wt >nul 2>nul",
"set NO_ANIMATIONS=1",
'if "%ERRORLEVEL%"=="0" (',
' wt --title Codewhale cmd /k "%~dp0codewhale-windows-x64.exe"',View on GitHub (pinned to 8880682c63)
Solutions
- Flatten the directory: move every file out of subdirectories so only regular files remain at the top level
- Replace symlinks with real copies (cp -L) before verify
- Re-run node scripts/release/assemble-release-assets.js --verify ASSET_DIR after cleanup
Example fix
# before: assets/codewhale-bundles/ (directory) triggers the error # after cp -L assets/codewhale-bundles/* assets/ rmdir assets/codewhale-bundles node scripts/release/assemble-release-assets.js --verify assets
Defensive patterns
Strategy: validation
Validate before calling
const fs = require("node:fs/promises");
async function listNonFileEntries(dir) {
const entries = await fs.readdir(dir, { withFileTypes: true });
return entries.filter((entry) => !entry.isFile()).map((entry) => entry.name);
}
// before verify:
const nonFiles = await listNonFileEntries(assetDir);
if (nonFiles.length > 0) throw new Error(`clean these non-file entries first: ${nonFiles.join(", ")}`); Prevention
- Copy artifact files, not folders, into the asset directory (cp dir/* rather than cp -r dir)
- Never symlink artifacts into the release directory — copy real bytes
- Check the directory with ls -la for stray mounts or dotfiles before verify in CI
When it happens
Trigger: A subdirectory inside the asset dir (e.g. codewhale-bundles/ copied wholesale instead of its contents); a symlinked binary (with withFileTypes the entry is a symlink, and isFile() is false for it); fifos, sockets, or device nodes created by accident.
Common situations: CI step copying a folder instead of its contents; local runs symlinking large artifacts to save disk; unzipping an archive that preserves a top-level directory; a mount point inside the directory.
Related errors
- Downloaded release artifacts are missing ${name} at ${source
- Output directory must be empty: ${outputDirectory}
- ${label} does not match the authoritative inventory; missing
- Downloaded release artifact must be a regular file: ${source
- CODEWHALE_SMOKE_ASSETS_DIR is not a directory: ${releaseAsse
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/dc67a08be4ebf062.
Report an issue: GitHub.