paperclipai/paperclip · error · Error
Could not prepare managed GitHub launchers
Error message
Could not prepare managed GitHub launchers
What it means
When provisioning managed GitHub launchers (git/gh shell wrappers) on a remote execution target, the module stages launcher files then runs `chmod 700 .../git .../gh && mkdir -p <configDir>`. This error is thrown when that chmod/mkdir command exits non-zero, so the launcher directory is unusable for GitHub operations.
Source
Thrown at packages/adapter-utils/src/execution-target.ts:1714
// the managed launchers after startup without loading a host user's profile.
const profile = `export PATH=${shellQuote(managedPath)}\n`;
const files: Record<string, string> = Object.fromEntries([
...["git", "gh"].map((name) => [name, githubLauncherSource()] as const),
...[".zshenv", ".zprofile", ".zshrc", ".bash_profile", ".bashrc", ".profile"].map((name) => [name, profile] as const),
]);
if (remote) {
const runner = adapterExecutionTargetCommandRunner(remote);
for (const [program, body] of Object.entries(files)) {
await syncRemoteTextFileWithHashSkip({
runner, remoteCwd: remote.remoteCwd, remoteDir: directory,
remotePath: path.posix.join(directory, program), body,
label: "GitHub operation launcher", action: "stage GitHub operation launcher",
lockDir: path.posix.join(directory, `.${program}.lock`),
timeoutMs: 15_000, shellCommand: adapterExecutionTargetShellCommand(remote),
});
}
const permissions = await runner.execute({ command: "sh", args: ["-c", `chmod 700 ${shellQuote(directory)}/git ${shellQuote(directory)}/gh && mkdir -p ${shellQuote(configDirectory)}`], cwd: remote.remoteCwd, timeoutMs: 15_000 });
if (permissions.exitCode !== 0) throw new Error("Could not prepare managed GitHub launchers");
} else {
await fs.mkdir(directory, { recursive: true, mode: 0o700 });
await fs.mkdir(configDirectory, { recursive: true, mode: 0o700 });
for (const [program, body] of Object.entries(files)) await fs.writeFile(path.join(directory, program), body, { mode: 0o700 });
}
return { ...input.env, PATH: managedPath, ZDOTDIR: directory, BASH_ENV: `${directory}/.bashrc`,
GH_CONFIG_DIR: configDirectory, PAPERCLIP_GITHUB_LAUNCHER_DIR: directory };
}
function buildBridgeResponseHeaders(response: Response): Record<string, string> {
const out: Record<string, string> = {};
// Keep `x-paperclip-bridge-outcome` in this list. The host marks a
// possibly-committed mutation with the `indeterminate` outcome. The in-sandbox
// server reads that header to map the 504 to a terminal 409. If the forward
// drops the header, the server keeps the retryable 504 and a caller that
// retries 5xx can repeat a mutation that already committed.
for (const key of ["content-type", "etag", "last-modified", "x-paperclip-bridge-outcome"]) {
const value = response.headers.get(key);View on GitHub (pinned to 01ad858492)
Solutions
- Exec into the target and run the chmod/mkdir command manually to see the failing sub-command and errno.
- Verify the remote user owns <directory> and that remote.remoteCwd exists and is writable.
- Check the mount is not read-only or mounted noexec (launchers must be executable).
- Re-run prepare to re-stage the launcher files if they were removed by a concurrent cleanup.
- Confirm 'sh' exists on the target and the lock dirs (.git.lock/.gh.lock) are not held by a stale process.
Example fix
// before
if (permissions.exitCode !== 0) throw new Error("Could not prepare managed GitHub launchers");
// after
if (permissions.exitCode !== 0) throw new Error(`Could not prepare managed GitHub launchers (exit ${permissions.exitCode}): ${permissions.stderr.slice(0, 500)}`); Defensive patterns
Strategy: try-catch
Validate before calling
const check = await runner.execute({ command: "sh", args: ["-c", `test -w ${shellQuote(directory)} && command -v chmod`], cwd: remote.remoteCwd, timeoutMs: 5_000 });
if (check.exitCode !== 0) throw new Error(`remote launcher dir not writable: ${directory}`); Type guard
null
Try / catch
try {
await prepareManagedGithubLaunchers(/* ... */);
} catch (err) {
if (String(err.message).includes("managed GitHub launchers")) {
log.error("chmod/mkdir failed on target; check ownership and mount flags", { remoteCwd: remote.remoteCwd });
}
throw err;
} Prevention
- Pre-create the launcher directory on the target with the same UID the runner executes as.
- Avoid noexec/read-only mounts for the launcher directory location.
- Stale .git.lock/.gh.lock dirs should be cleaned between runs.
- Run the exact chmod/mkdir line manually once when provisioning a new target.
When it happens
Trigger: runner.execute({command:'sh', args:['-c', 'chmod 700 <dir>/git <dir>/gh && mkdir -p <configDir>'], cwd: remote.remoteCwd}) returns exitCode !== 0. Also implied when any earlier staging step (writing launcher files via the same runner) fails before the chmod line.
Common situations: Remote user lacks write/execute permission on the launcher directory or remote.remoteCwd; the directory is on a read-only or noexec mount; the remote 'sh' is missing; the staging directory was deleted between staging and chmod (race with cleanup); disk full preventing file creation.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- GitHub inventory failed: the active installation has not gra
- grant_owner_membership_inactive
- [opencode-local] Remote model availability probe for "${mode
- Registered base project workspace Paperclip config at ${conf
- [paperclip] shapePaperclipWorkspaceEnvForExecution called wi
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/304ecfc0a36da962.
Report an issue: GitHub.