paperclipai/paperclip · error · Error
Materialized OpenCode executable has unsafe permissions
Error message
Materialized OpenCode executable has unsafe permissions
What it means
After verifying the digest, the script checks the target's permission bits: it must be a regular file, executable (any of 0o111 set), and not group/world writable (no 0o022 bits). Anything else is a tampering/correctness risk and throws.
Source
Thrown at packages/paperclip-runner/scripts/materialize-opencode-binary.mjs:87
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 };
}
const invokedPath = process.argv[1]
? pathToFileURL(realpathSync(process.argv[1])).href
: null;View on GitHub (pinned to 01ad858492)
Solutions
- Confirm the filesystem honors chmod ('chmod 755 bin/opencode.exe && stat -c %a bin/opencode.exe'); build on a local ext4/overlayfs volume if not.
- Remove the target and re-run the materialization to re-apply 0o755 cleanly.
- Check for tools modifying permissions on node_modules (ACLs, sync tools) and exclude the directory.
- If the file is no longer a regular file, treat it as tampering: delete it, reinstall dependencies, re-run.
Example fix
# before $ stat -c %a bin/opencode.exe # e.g. 775 (group-writable) # after $ chmod 755 bin/opencode.exe $ node scripts/materialize-opencode-binary.mjs
Defensive patterns
Strategy: validation
Validate before calling
import { lstatSync, chmodSync } from 'node:fs';
const st = lstatSync(target);
const mode = st.mode & 0o777;
if (!st.isFile() || (mode & 0o111) === 0 || (mode & 0o022) !== 0) {
chmodSync(target, 0o755);
} Try / catch
try {
materializePinnedOpenCodeBinary();
} catch (err) {
if (err.message === 'Materialized OpenCode executable has unsafe permissions') {
chmodSync(target, 0o755);
materializePinnedOpenCodeBinary();
} else throw err;
} Prevention
- Use a sane umask (e.g. 022) in build scripts and CI.
- Build on filesystems that honor chmod (local disk, standard overlayfs).
- Check ACLs/sync tools that may add group-write bits to node_modules.
- Re-apply 0o755 after any post-link tooling touches the binary.
When it happens
Trigger: The target after chmodSync(0o755) still has an unsafe mode — e.g. the filesystem ignored the chmod (some mounts), a umask/ACL forced group-write bits, or the target was replaced by a non-file between link and stat (TOCTOU).
Common situations: Building on a mounted volume that does not honor chmod; an over-permissive umask; ACLs or sync tools adding write bits to node_modules; another process swapping the binary.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- ACPX ${agent} runtime executable must be a bounded executabl
- Registered base project workspace Paperclip config at ${conf
- A trusted viewer build is required for public chat reports
- Trusted viewer must not use symlinks
- Unexpected trusted viewer asset
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/2db70d9ca24bb840.
Report an issue: GitHub.