paperclipai/paperclip · error · Error

OpenCode target executable is not a regular file

Error message

OpenCode target executable is not a regular file

What it means

When the materialized target bin/opencode.exe already exists, the script requires it to be a regular file before unlinking and re-linking. If the existing target is a symlink, directory, or other non-regular file, it refuses to delete it blindly and throws.

Source

Thrown at packages/paperclip-runner/scripts/materialize-opencode-binary.mjs:64

  }

  const packageRoot = realpathSync(
    options.packageRoot ??
      resolve(import.meta.dirname, "../node_modules/opencode-ai"),
  );
  const dependencyRoot = dirname(packageRoot);
  const baselineRoot = realpathSync(join(dependencyRoot, BASELINE_PACKAGE));
  assertPackage(packageRoot, "opencode-ai");
  assertPackage(baselineRoot, BASELINE_PACKAGE);

  const source = join(baselineRoot, "bin", "opencode");
  const target = join(packageRoot, "bin", "opencode.exe");
  if (!lstatSync(source).isFile()) {
    throw new Error("Pinned OpenCode source executable is not a regular file");
  }
  if (existsSync(target)) {
    if (!lstatSync(target).isFile()) {
      throw new Error("OpenCode target executable is not a regular file");
    }
    unlinkSync(target);
  }
  try {
    linkSync(source, target);
  } catch (error) {
    const code = error?.code;
    if (!new Set(["EACCES", "EMLINK", "EPERM", "EXDEV"]).has(code)) {
      throw error;
    }
    copyFileSync(source, target);
  }
  chmodSync(target, 0o755);

  const sourceDigest = sha256(source);
  const targetDigest = sha256(target);
  if (sourceDigest !== targetDigest) {
    throw new Error("Materialized OpenCode executable digest mismatch");

View on GitHub (pinned to 01ad858492)

Solutions

  1. Inspect 'ls -la <packageRoot>/bin/' and manually remove the non-regular opencode.exe ('rm -rf' for a directory/symlink).
  2. Re-run materializePinnedOpenCodeBinary so it creates a fresh hard link from the verified source.
  3. If node_modules state is suspect, reinstall the opencode-ai package to reset its bin directory.
  4. Audit what created the non-file target and stop it from writing into the package bin dir.

Example fix

# before (broken state)
$ ls -la bin/opencode.exe  # symlink or directory
# after
$ rm -rf bin/opencode.exe
$ node scripts/materialize-opencode-binary.mjs
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, lstatSync, rmSync } from 'node:fs';
const target = join(pkgRoot, 'bin', 'opencode.exe');
if (existsSync(target) && !lstatSync(target).isFile()) {
  rmSync(target, { recursive: true }); // clear non-regular stale target first
}

Type guard

function isClearableTarget(p) {
  try { return !existsSync(p) || lstatSync(p).isFile(); } catch { return false; }
}

Try / catch

try {
  materializePinnedOpenCodeBinary();
} catch (err) {
  if (err.message === 'OpenCode target executable is not a regular file') {
    rmSync(join(pkgRoot, 'bin', 'opencode.exe'), { recursive: true, force: true });
    materializePinnedOpenCodeBinary();
  } else throw err;
}

Prevention

When it happens

Trigger: A previous failed/partial materialization or an external tool left opencode.exe in the package's bin directory as a symlink or directory, and materializePinnedOpenCodeBinary runs again.

Common situations: A leftover directory named opencode.exe from a bad unzip; a wrapper script or tool replaced the binary with a symlink; an interrupted prior run left corrupt node_modules state.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/99b3f10788c3c235. Report an issue: GitHub.