paperclipai/paperclip · critical · Error

Materialized OpenCode executable digest mismatch

Error message

Materialized OpenCode executable digest mismatch

What it means

After hard-linking and chmod 0o755, the script recomputes SHA-256 digests of source and target and requires them to be identical, proving the target holds the pinned bytes. A mismatch means the materialized executable is not the verified pinned build, so it throws.

Source

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

      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");
  }
  const targetStat = lstatSync(target);
  const mode = targetStat.mode & 0o777;
  if (!targetStat.isFile() || (mode & 0o111) === 0 || mode & 0o022) {
    throw new Error("Materialized OpenCode executable has unsafe permissions");
  }

  const version = spawnSync(target, ["--version"], {
    encoding: "utf8",
    timeout: 30_000,
    windowsHide: true,
  });
  if (version.status !== 0 || version.stdout.trim() !== OPENCODE_VERSION) {
    throw new Error(
      `Materialized OpenCode executable did not report ${OPENCODE_VERSION}`,
    );
  }
  return { sourceDigest, target, version: OPENCODE_VERSION };

View on GitHub (pinned to 01ad858492)

Solutions

  1. Delete the target (rm <packageRoot>/bin/opencode.exe) and re-run materializePinnedOpenCodeBinary on a local filesystem.
  2. Verify the source binary itself is intact against the upstream pinned release digest; reinstall the baseline package if mutated.
  3. Run outside volumes/containers that break hard links (check with 'stat -c %i' that source and target share an inode).
  4. Exclude the package bin directory from antivirus/on-access scanning during build.

Example fix

# before
$ sha256sum bin/opencode.exe  # differs from baseline bin/opencode
# after
$ rm bin/opencode.exe && node scripts/materialize-opencode-binary.mjs
$ sha256sum bin/opencode.exe  # matches source digest
Defensive patterns

Strategy: validation

Validate before calling

import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
const digest = (p) => createHash('sha256').update(readFileSync(p)).digest('hex');
if (digest(source) !== digest(target)) throw new Error('pre-check: materialized binary digest mismatch');

Try / catch

try {
  materializePinnedOpenCodeBinary();
} catch (err) {
  if (err.message === 'Materialized OpenCode executable digest mismatch') {
    // treat as tampering/corruption: rebuild from a clean install
    execSync('pnpm install --force');
    materializePinnedOpenCodeBinary();
  } else throw err;
}

Prevention

When it happens

Trigger: The target file's digest differs from the source after linking — e.g. the target was modified between link and digest, a stale different binary is at the target, or the filesystem broke hard-link semantics.

Common situations: Antivirus or build tooling rewrote the freshly linked binary; building inside a container/volume where hard links degrade to copies; a race where another process replaced opencode.exe concurrently.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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