Hmbown/CodeWhale · error · Error

Release asset directory must be flat; found

Error message

Release asset directory must be flat; found: ${nonFiles.map((entry) => entry.name).join(", ")}

What it means

verifyAssetDirectory reads the release asset directory with withFileTypes and requires every entry to be a regular file — the layout must be flat. Any subdirectory, symlink, or other non-file entry causes this Error listing the offending entry names.

Solutions

  1. Flatten the directory so every expected asset is a regular file directly in it; remove or move subdirectories.
  2. Replace symlinks with real copies of the files.
  3. Point --input/--output at the correct flat artifact directory produced by the download step.

Example fix

// before
assets/linux/app.zip  (nested dir)
// after
mv assets/linux/app.zip assets/app.zip && rmdir assets/linux
Defensive patterns

Strategy: validation

Validate before calling

const entries = await fs.readdir(dir, { withFileTypes: true });
if (entries.some(e => !e.isFile())) throw new Error('asset dir must contain only regular files');

Try / catch

try { await verifyAssetDirectory(dir); } catch (e) { if (String(e.message).includes('must be flat')) { console.error('Flatten the asset directory:', e.message); process.exitCode = 1; } else throw e; }

Prevention

When it happens

Trigger: Running assemble against a directory containing subdirectories (e.g. nested platform folders) or symlinks left by a download/unpack step.

Common situations: Pointing the script at a raw download dir with per-OS subfolders instead of the flattened artifact directory, or a symlinked output path.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/dc67a08be4ebf062. Report an issue: GitHub.

Appendix: 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 433685b202)