Hmbown/CodeWhale · error · Error

Downloaded release artifact must be a regular file

Error message

Downloaded release artifact must be a regular file: ${source}

What it means

assemble lstat()s each expected intermediate artifact and, after confirming it exists, checks that it is a regular file. Directories, symlinks, sockets, or other special files at the expected path throw this Error naming the path.

Solutions

  1. Inspect the reported path; remove the non-file entry and place the real asset file there.
  2. Replace symlinks with actual file copies before running assemble.
  3. Fix the download step so it writes regular files with the exact expected asset names.

Example fix

// before
app.zip -> /cache/partial/  (symlink)
// after
cp /cache/downloads/app.zip assets/app.zip
Defensive patterns

Strategy: validation

Validate before calling

for (const name of allReleaseAssetNames()) {
  const st = await fs.lstat(path.join(inputDir, name));
  if (!st.isFile()) throw new Error(`${name} is not a regular file`);
}

Try / catch

try { await assemble(...); } catch (e) { if (String(e.message).startsWith('Downloaded release artifact must be a regular file')) { console.error('Replace the non-file entry:', e.message); process.exitCode = 1; } else throw e; }

Prevention

When it happens

Trigger: A path where an asset file is expected actually contains a directory or symlink, e.g. the downloader created a folder named like the asset, or a symlink from an unpack step.

Common situations: Download tooling writing to a directory of the same name, or a misconfigured artifact extraction leaving symlinks instead of files.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

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

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));
  }
  await fs.writeFile(
    path.join(outputDirectory, WINDOWS_LAUNCHER),
    windowsLauncherContents(),
    "utf8",
  );

View on GitHub (pinned to 433685b202)