paperclipai/paperclip · error · Error
ACPX ${agent} runtime executable could not be opened as a no
Error message
ACPX ${agent} runtime executable could not be opened as a no-follow regular file What it means
After the lexical lstat check passes, the runtime opens the executable with `O_NOFOLLOW` (via verifiedExecutableOpenFlags) so the kernel refuses to traverse a symlink swapped in between the lstat and the open (TOCTOU defense). If `open` throws for any reason — O_NOFOLLOW rejection, EACCES, ENOENT from a swap, etc. — the library cannot guarantee a safe handle and throws this error instead of surfacing the raw errno.
Source
Thrown at packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts:1038
);
if (
lexicalBefore === null ||
lexicalBefore.isSymbolicLink() ||
!lexicalBefore.isFile()
) {
throw new Error(
`ACPX ${agent} runtime executable must be a real regular file`,
);
}
let handle: FileHandle;
try {
handle = await open(
executablePath,
verifiedExecutableOpenFlags(process.platform, constants.O_NOFOLLOW),
);
} catch {
throw new Error(
`ACPX ${agent} runtime executable could not be opened as a no-follow regular file`,
);
}
try {
const before = await handle.stat({ bigint: true });
if (
!before.isFile() ||
before.size < 1n ||
before.size > BigInt(MAX_ACPX_RUNTIME_EXECUTABLE_BYTES) ||
(before.mode & 0o111n) === 0n
) {
throw new Error(
`ACPX ${agent} runtime executable must be a bounded executable file`,
);
}
const hash = createHash("sha256");
const buffer = Buffer.alloc(1024 * 1024);View on GitHub (pinned to 01ad858492)
Solutions
- Re-run the runtime start after the concurrent install/upgrade finishes — the file was likely being swapped
- Check file ownership/permissions: the service user needs read (and open) access to the binary and its parent dirs
- Confirm no security module (SELinux/AppArmor) is denying opens of the runtime path
- Ensure the binary lives on a local filesystem supporting O_NOFOLLOW semantics, not an exotic mount
- Reinstall the ACPX runtime atomically so no partial/symlinked state exists
Example fix
// before: binary replaced by a symlink mid-upgrade ls -l /usr/local/bin/acpx # acpx -> /opt/acpx-next/bin/acpx (ELOOP on open) // after: wait for upgrade to complete and verify lstat /opt/acpx/bin/acpx # regular file, not symlink /opt/acpx/bin/acpx --version
Defensive patterns
Strategy: retry
Validate before calling
import { accessSync, constants as fsConstants } from 'node:fs';
function pathReadable(p: string): boolean {
try { accessSync(p, fsConstants.R_OK); return true; } catch { return false; }
} Try / catch
try {
await startAcppRuntime();
} catch (e) {
if (String(e?.message).includes('could not be opened as a no-follow regular file')) {
await waitForUpgradeToFinish(); // concurrent install/swap likely
await startAcppRuntime(); // retry once
} else {
throw e;
}
} Prevention
- Avoid starting runs while an install/upgrade of the runtime is in progress
- Grant the service user read access to the binary and parent directories
- Check SELinux/AppArmor audit logs if opens are denied on hardened hosts
- Use atomic installs (temp file + rename) so O_NOFOLLOW never trips on a swap
When it happens
Trigger: `fs.open(executablePath, verifiedExecutableOpenFlags(process.platform, O_NOFOLLOW))` rejects: the path was replaced by a symlink after lstat (ELOOP), permissions changed (EACCES), the file was deleted between lstat and open (ENOENT), or the platform lacks a required open flag.
Common situations: Concurrent install/upgrade process swaps the binary for a symlink while a run is starting; restrictive umask or ownership makes the file unopenable by the service user; hardened environments (SELinux/AppArmor) deny open; NFS/network filesystems where symlink semantics or open flags behave unexpectedly.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- ACPX ${agent} runtime executable must be a real regular file
- ACPX ${agent} runtime executable must be a bounded executabl
- ACPX ${agent} runtime executable changed while it was verifi
- Materialized OpenCode executable has unsafe permissions
- A trusted viewer build is required for public chat reports
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/844fcb4e657fb7cf.
Report an issue: GitHub.