{"record":{"id":"ebb3ed3472e104f4","repo":"paperclipai/paperclip","slug":"materialized-opencode-executable-digest-mismatch","errorCode":null,"errorMessage":"Materialized OpenCode executable digest mismatch","messagePattern":"Materialized OpenCode executable digest mismatch","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/paperclip-runner/scripts/materialize-opencode-binary.mjs","lineNumber":82,"sourceCode":"      throw new Error(\"OpenCode target executable is not a regular file\");\n    }\n    unlinkSync(target);\n  }\n  try {\n    linkSync(source, target);\n  } catch (error) {\n    const code = error?.code;\n    if (!new Set([\"EACCES\", \"EMLINK\", \"EPERM\", \"EXDEV\"]).has(code)) {\n      throw error;\n    }\n    copyFileSync(source, target);\n  }\n  chmodSync(target, 0o755);\n\n  const sourceDigest = sha256(source);\n  const targetDigest = sha256(target);\n  if (sourceDigest !== targetDigest) {\n    throw new Error(\"Materialized OpenCode executable digest mismatch\");\n  }\n  const targetStat = lstatSync(target);\n  const mode = targetStat.mode & 0o777;\n  if (!targetStat.isFile() || (mode & 0o111) === 0 || mode & 0o022) {\n    throw new Error(\"Materialized OpenCode executable has unsafe permissions\");\n  }\n\n  const version = spawnSync(target, [\"--version\"], {\n    encoding: \"utf8\",\n    timeout: 30_000,\n    windowsHide: true,\n  });\n  if (version.status !== 0 || version.stdout.trim() !== OPENCODE_VERSION) {\n    throw new Error(\n      `Materialized OpenCode executable did not report ${OPENCODE_VERSION}`,\n    );\n  }\n  return { sourceDigest, target, version: OPENCODE_VERSION };","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/materialize-opencode-binary.mjs#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete the target (rm <packageRoot>/bin/opencode.exe) and re-run materializePinnedOpenCodeBinary on a local filesystem.","Verify the source binary itself is intact against the upstream pinned release digest; reinstall the baseline package if mutated.","Run outside volumes/containers that break hard links (check with 'stat -c %i' that source and target share an inode).","Exclude the package bin directory from antivirus/on-access scanning during build."],"exampleFix":"# before\n$ sha256sum bin/opencode.exe  # differs from baseline bin/opencode\n# after\n$ rm bin/opencode.exe && node scripts/materialize-opencode-binary.mjs\n$ sha256sum bin/opencode.exe  # matches source digest","handlingStrategy":"validation","validationCode":"import { createHash } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nconst digest = (p) => createHash('sha256').update(readFileSync(p)).digest('hex');\nif (digest(source) !== digest(target)) throw new Error('pre-check: materialized binary digest mismatch');","typeGuard":null,"tryCatchPattern":"try {\n  materializePinnedOpenCodeBinary();\n} catch (err) {\n  if (err.message === 'Materialized OpenCode executable digest mismatch') {\n    // treat as tampering/corruption: rebuild from a clean install\n    execSync('pnpm install --force');\n    materializePinnedOpenCodeBinary();\n  } else throw err;\n}","preventionTips":["Verify the upstream pinned binary digest against the release checksum before materializing.","Build on local filesystems, not network/overlay mounts that break hard links.","Exclude package bin dirs from antivirus on-access rewriting.","Fail closed: never run a binary whose digest check failed."],"tags":["security","integrity","checksum","supply-chain"],"backgroundTag":"checksum-mismatch","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}