Hmbown/CodeWhale · error · Error

Downloaded release artifacts are missing ${name} at ${source

Error message

Downloaded release artifacts are missing ${name} at ${source}

What it means

During assemble(), an expected release asset was not found at its computed intermediate path: bundle assets and BUNDLE_CHECKSUM_MANIFEST live at INPUT_DIR/codewhale-bundles/<name>, everything else at INPUT_DIR/<name>/<name>. The ENOENT is converted into this named error with the exact path, meaning the CI artifact-download step did not deliver every expected file.

Source

Thrown at scripts/release/assemble-release-assets.js:130

  if (name === BUNDLE_CHECKSUM_MANIFEST || BUNDLE_ASSET_NAMES.includes(name)) {
    return path.join(inputDirectory, "codewhale-bundles", name);
  }
  return path.join(inputDirectory, name, name);
}

async function assemble(inputDirectory, outputDirectory) {
  const expected = allReleaseAssetNames();
  const generated = new Set([WINDOWS_LAUNCHER, CHECKSUM_MANIFEST]);
  const copiedNames = expected.filter((name) => !generated.has(name));
  const sources = new Map();
  for (const name of copiedNames) {
    const source = intermediateArtifactPath(inputDirectory, name);
    let sourceStat;
    try {
      sourceStat = await fs.lstat(source);
    } catch (error) {
      if (error && error.code === "ENOENT") {
        throw new Error(`Downloaded release artifacts are missing ${name} at ${source}`);
      }
      throw error;
    }
    if (!sourceStat.isFile()) {
      throw new Error(`Downloaded release artifact must be a regular file: ${source}`);
    }
    sources.set(name, source);
  }

  await fs.mkdir(outputDirectory, { recursive: true });
  const existing = await fs.readdir(outputDirectory);
  if (existing.length > 0) {
    throw new Error(`Output directory must be empty: ${outputDirectory}`);
  }

  for (const name of copiedNames) {
    await fs.copyFile(sources.get(name), path.join(outputDirectory, name));
  }

View on GitHub (pinned to 8880682c63)

Solutions

  1. Check the path in the message and place the file at INPUT_DIR/<name>/<name> (or INPUT_DIR/codewhale-bundles/<name> for bundles)
  2. Re-run the full build workflow so every artifact is uploaded, then re-download all artifacts into INPUT_DIR before assemble
  3. Verify the upload workflow's artifact names still cover allReleaseAssetNames()
  4. Pass the directory that directly contains the per-artifact subfolders — not the workspace root

Example fix

# before: missing INPUT_DIR/codewhale-aarch64-linux/codewhale-aarch64-linux.tar.gz

# after: re-download every workflow artifact into a clean input dir
rm -rf artifacts-in && mkdir artifacts-in
gh run download <run-id> -D artifacts-in
node scripts/release/assemble-release-assets.js artifacts-in release-out
Defensive patterns

Strategy: validation

Validate before calling

const fs = require("node:fs/promises");
const path = require("node:path");
const {
  allReleaseAssetNames,
  BUNDLE_ASSET_NAMES,
  BUNDLE_CHECKSUM_MANIFEST,
  CHECKSUM_MANIFEST,
} = require("./npm/codewhale/scripts/artifacts");
function intermediateArtifactPath(inputDir, name) {
  return name === BUNDLE_CHECKSUM_MANIFEST || BUNDLE_ASSET_NAMES.includes(name)
    ? path.join(inputDir, "codewhale-bundles", name)
    : path.join(inputDir, name, name);
}
async function assertArtifactsPresent(inputDir) {
  const missing = [];
  for (const name of allReleaseAssetNames()) {
    if (name === "codewhale.bat" || name === CHECKSUM_MANIFEST) continue; // generated at assemble time
    try {
      await fs.access(intermediateArtifactPath(inputDir, name));
    } catch {
      missing.push(intermediateArtifactPath(inputDir, name));
    }
  }
  if (missing.length > 0) throw new Error(`input directory is missing: ${missing.join(", ")}`);
}

Prevention

When it happens

Trigger: Running assemble with an INPUT_DIR lacking e.g. INPUT_DIR/codewhale-windows-x64.exe/codewhale-windows-x64.exe or INPUT_DIR/codewhale-bundles/<bundle>.tar.gz; an upload step that failed silently; artifact names renamed in the workflow; passing the wrong directory (repo root or workspace) as INPUT_DIR.

Common situations: actions/download-artifact fetching only some artifacts because an upload step was skipped on a matrix leg; artifact name drift after workflow edits; downloading to a nested path and passing its parent.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/dd69d46ddf7783a9. Report an issue: GitHub.