paperclipai/paperclip · error · Error

Systemd service values must not contain line breaks

Error message

Systemd service values must not contain line breaks

What it means

Thrown by escapeSystemd when a value being interpolated into a systemd unit file contains a carriage return (\r) or newline (\n). Systemd unit files are line-oriented; inserting a newline into a value field could inject additional directives (e.g., a value containing '\nExecStart=malicious-command'). This is a fail-closed injection-prevention guard applied before any escaping occurs.

Source

Thrown at cli/src/services/service-manager.ts:57

export type CommandResult = { stdout: string; stderr: string };
export type CommandRunner = (command: string, args: string[], options?: { inherit?: boolean }) => Promise<CommandResult>;

export const defaultCommandRunner: CommandRunner = async (command, args, options) => {
  if (options?.inherit) {
    await new Promise<void>((resolve, reject) => {
      const child = execFile(command, args, { windowsHide: true }, (error) => error ? reject(error) : resolve());
      child.stdout?.pipe(process.stdout);
      child.stderr?.pipe(process.stderr);
    });
    return { stdout: "", stderr: "" };
  }
  const result = await execFileAsync(command, args, { encoding: "utf8", windowsHide: true });
  return { stdout: result.stdout, stderr: result.stderr };
};

function escapeSystemd(value: string): string {
  if (/\r|\n/.test(value)) {
    throw new Error("Systemd service values must not contain line breaks");
  }
  return value
    .replaceAll("\\", "\\\\")
    .replaceAll('"', '\\"')
    .replaceAll("$", () => "$$")
    .replaceAll("%", "%%");
}

function escapeXml(value: string): string {
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
}

function escapeRegExp(value: string): string {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

export function resolveServiceShimPath(homeDir = os.homedir()): string {
  return process.env.PAPERCLIP_SHIM_PATH?.trim() || path.join(homeDir, ".local", "bin", "paperclipai");

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Check the environment variables: 'echo "$PAPERCLIP_INSTANCE_ID" | cat -v' to reveal hidden characters.
  2. Strip newlines from the instance ID before passing it: ensure the --instance flag value has no line breaks.
  3. If PAPERCLIP_HOME or the shim path contains newlines, fix the environment to use clean single-line paths.
  4. Use a simple alphanumeric instance ID: instance IDs should match the expected identifier format.

Example fix

// before: instance ID contains a trailing newline
// PAPERCLIP_INSTANCE_ID='default\n'

// after: clean instance ID
// PAPERCLIP_INSTANCE_ID='default'

// or sanitize before use:
const cleanInstanceId = instanceId.replace(/[\r\n]/g, '');
Defensive patterns

Strategy: validation

Validate before calling

function isSafeSystemdValue(value: string): boolean {
  return !/\r|\n/.test(value);
}

// Call before renderSystemdUnit / SystemdServiceManager construction:
const fields = [instanceId, shimPath, homeDir];
for (const field of fields) {
  if (!isSafeSystemdValue(field)) {
    throw new Error(`Field contains line breaks and cannot be used in a systemd unit: ${JSON.stringify(field)}`);
  }
}

Try / catch

try {
  const unit = renderSystemdUnit({ instanceId, shimPath, homeDir });
} catch (error) {
  if (error instanceof Error && error.message === 'Systemd service values must not contain line breaks') {
    // Sanitize the offending field
    const cleanInstanceId = instanceId.replace(/[\r\n]/g, '');
    // Retry with cleaned values
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling renderSystemdUnit or any code path that calls escapeSystemd with an instanceId, shimPath, or homeDir that contains a newline or carriage return character. This includes the SystemdServiceManager constructor and its renderDefinition/install/start/restart methods.

Common situations: PAPERCLIP_INSTANCE_ID is set to a value containing a newline (e.g., from a misconfigured CI variable or a copy-paste with trailing newline). PAPERCLIP_HOME or PAPERCLIP_SHIM_PATH contains a newline. An attacker or broken script passes a crafted instance ID.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/7b9599699cf3a426. Report an issue: GitHub.