{"record":{"id":"dc67a08be4ebf062","repo":"Hmbown/CodeWhale","slug":"release-asset-directory-must-be-flat-found-nonfiles-map","errorCode":null,"errorMessage":"Release asset directory must be flat; found: ${nonFiles.map((entry) => entry.name).join(\", \")}","messagePattern":"Release asset directory must be flat; found: (.+?)","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/release/assemble-release-assets.js","lineNumber":85,"sourceCode":"  const manifestPath = path.join(directory, manifestName);\n  const checksums = parseChecksumManifest(\n    await fs.readFile(manifestPath, \"utf8\"),\n    manifestName,\n  );\n  assertExactNames([...checksums.keys()], expectedNames, manifestName);\n  for (const name of expectedNames) {\n    const actual = await sha256(path.join(directory, name));\n    if (checksums.get(name) !== actual) {\n      throw new Error(`${manifestName} checksum mismatch for ${name}`);\n    }\n  }\n}\n\nasync function verifyAssetDirectory(directory) {\n  const entries = await fs.readdir(directory, { withFileTypes: true });\n  const nonFiles = entries.filter((entry) => !entry.isFile());\n  if (nonFiles.length > 0) {\n    throw new Error(\n      `Release asset directory must be flat; found: ${nonFiles.map((entry) => entry.name).join(\", \")}`,\n    );\n  }\n\n  const expected = allReleaseAssetNames();\n  assertExactNames(entries.map((entry) => entry.name), expected, \"Release asset directory\");\n  await assertManifest(directory, CHECKSUM_MANIFEST, checksummedReleaseAssetNames());\n  await assertManifest(directory, BUNDLE_CHECKSUM_MANIFEST, BUNDLE_ASSET_NAMES);\n  console.log(`Verified ${expected.length} release assets in ${directory}`);\n}\n\nfunction windowsLauncherContents() {\n  return [\n    \"@echo off\",\n    \"where wt >nul 2>nul\",\n    \"set NO_ANIMATIONS=1\",\n    'if \"%ERRORLEVEL%\"==\"0\" (',\n    '    wt --title Codewhale cmd /k \"%~dp0codewhale-windows-x64.exe\"',","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/scripts/release/assemble-release-assets.js#L67-L103","documentation":"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.","triggerScenarios":"Running assemble against a directory containing subdirectories (e.g. nested platform folders) or symlinks left by a download/unpack step.","commonSituations":"Pointing the script at a raw download dir with per-OS subfolders instead of the flattened artifact directory, or a symlinked output path.","solutions":["Flatten the directory so every expected asset is a regular file directly in it; remove or move subdirectories.","Replace symlinks with real copies of the files.","Point --input/--output at the correct flat artifact directory produced by the download step."],"exampleFix":"// before\nassets/linux/app.zip  (nested dir)\n// after\nmv assets/linux/app.zip assets/app.zip && rmdir assets/linux","handlingStrategy":"validation","validationCode":"const entries = await fs.readdir(dir, { withFileTypes: true });\nif (entries.some(e => !e.isFile())) throw new Error('asset dir must contain only regular files');","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Flatten downloaded artifacts before assembly.","Copy over symlinks so they become real files.","Assert directory flatness as an early CI step."],"tags":["release","filesystem","validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}