paperclipai/paperclip · error · Error
Refusing to write service definition in directory not owned
Error message
Refusing to write service definition in directory not owned by the current user: ${directoryPath}. What it means
Thrown by writeIfChanged when the service definition directory's owner UID does not match the current process's UID (process.getuid()). Writing a systemd/launchd service definition into a directory owned by another user could allow privilege escalation if that user controls the directory contents. This check is skipped on platforms where process.getuid is undefined (e.g., Windows).
Source
Thrown at cli/src/services/service-manager.ts:144
</dict>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>ThrottleInterval</key><integer>5</integer>
<key>ExitTimeOut</key><integer>300</integer>
<key>StandardOutPath</key><string>${escapeXml(input.stdoutPath)}</string>
<key>StandardErrorPath</key><string>${escapeXml(input.stderrPath)}</string>
</dict>
</plist>
`;
}
async function writeIfChanged(filePath: string, contents: string): Promise<boolean> {
const directoryPath = path.dirname(filePath);
await fs.mkdir(directoryPath, { recursive: true, mode: 0o700 });
const directoryStat = await fs.lstat(directoryPath);
if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) throw new Error(`Refusing to write service definition through unsafe directory ${directoryPath}.`);
const currentUid = process.getuid?.();
if (currentUid !== undefined && directoryStat.uid !== currentUid) throw new Error(`Refusing to write service definition in directory not owned by the current user: ${directoryPath}.`);
try {
const stat = await fs.lstat(filePath);
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink > 1) throw new Error(`Refusing to replace unsafe service definition ${filePath}.`);
if (currentUid !== undefined && stat.uid !== currentUid) throw new Error(`Refusing to replace service definition not owned by the current user: ${filePath}.`);
if (await fs.readFile(filePath, "utf8") === contents) return false;
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
const temporaryPath = path.join(directoryPath, `.${path.basename(filePath)}.tmp-${process.pid}-${Date.now()}`);
try {
await fs.writeFile(temporaryPath, contents, { encoding: "utf8", mode: 0o644, flag: "wx" });
await fs.rename(temporaryPath, filePath);
} finally {
await fs.rm(temporaryPath, { force: true });
}
return true;
}
View on GitHub (pinned to 67001ec6eb)
Solutions
- Check ownership: 'ls -la ~/.config/systemd/user/' or 'ls -la ~/Library/LaunchAgents/' and compare the owner UID to your current UID ('id -u').
- Fix ownership: 'chown -R $(id -u):$(id -g) ~/.config/systemd/user/'.
- If you previously ran with sudo, re-run without sudo, or fix ownership of the affected directories.
- Verify you are running as the correct user account for this Paperclip instance.
Example fix
// before: directory owned by root // ls -la ~/.config/systemd/user/ -> owner: root // after: fix ownership to current user // sudo chown -R $(id -u):$(id -g) ~/.config/systemd/user/ // chmod 700 ~/.config/systemd/user/
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
import path from 'node:path';
function isDirOwnedByCurrentUser(dirPath: string): boolean {
const currentUid = process.getuid?.();
if (currentUid === undefined) return true; // skip on platforms without getuid
try {
const stat = fs.lstatSync(dirPath);
return stat.isDirectory() && !stat.isSymbolicLink() && stat.uid === currentUid;
} catch {
return false;
}
}
// Call before service install:
const serviceDir = path.dirname(definitionPath);
if (!isDirOwnedByCurrentUser(serviceDir)) {
throw new Error(`Run 'chown -R $(id -u):$(id -g) ${serviceDir}' and retry.`);
} Try / catch
try {
await manager.install({ startNow: true, startOnLogin: true });
} catch (error) {
if (error instanceof Error && error.message.includes('not owned by the current user')) {
console.error('Fix directory ownership: sudo chown -R $(id -u):$(id -g)', path.dirname(manager.definitionPath));
}
throw error;
} Prevention
- Never run 'paperclipai service install' with sudo—it can create root-owned service directories.
- If directories were created by root, fix ownership with 'sudo chown -R $(id -u):$(id -g) ~/.config/systemd/user/'.
- In containers, ensure the runtime UID matches the UID that owns the service directories.
When it happens
Trigger: Calling install/start/restart on a service manager when ~/.config/systemd/user/ or ~/Library/LaunchAgents/ is owned by a different user (e.g., root or another account). This commonly occurs when running as a different user than the one that created the directory, or after a user migration.
Common situations: Running paperclipai service commands with sudo (which may set ownership to root). The service directory was created by a different user account. Home directory ownership was changed (e.g., chown -R root ~). Running inside a container where UID mapping differs from the host.
Related errors
- Refusing to write service definition through unsafe director
- Refusing to replace unsafe service definition ${filePath}.
- Systemd service values must not contain line breaks
- Refusing to modify path not owned by the current user: ${tar
- Refusing to write export file outside output directory: ${re
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/1f8b691beab89a7b.
Report an issue: GitHub.