paperclipai/paperclip · error · Error

Invalid retention days '${String(candidate)}'. Use a positiv

Error message

Invalid retention days '${String(candidate)}'. Use a positive integer.

What it means

Thrown by normalizeRetentionDays (db-backup.ts:40) when the resolved retention value is not a positive integer. Used by the db:backup command to compute how many days of backups to keep; the candidate is the explicit option or the configured fallback.

Source

Thrown at cli/src/commands/db-backup.ts:40

  const envUrl = process.env.DATABASE_URL?.trim();
  if (envUrl) return { value: envUrl, source: "DATABASE_URL" };

  const config = readConfig(configPath);
  if (config?.database.mode === "postgres" && config.database.connectionString?.trim()) {
    return { value: config.database.connectionString.trim(), source: "config.database.connectionString" };
  }

  const port = config?.database.embeddedPostgresPort ?? 54329;
  return {
    value: `postgres://paperclip:paperclip@127.0.0.1:${port}/paperclip`,
    source: `embedded-postgres@${port}`,
  };
}

function normalizeRetentionDays(value: number | undefined, fallback: number): number {
  const candidate = value ?? fallback;
  if (!Number.isInteger(candidate) || candidate < 1) {
    throw new Error(`Invalid retention days '${String(candidate)}'. Use a positive integer.`);
  }
  return candidate;
}

function resolveBackupDir(raw: string): string {
  return path.resolve(expandHomePrefix(raw.trim()));
}

export async function dbBackupCommand(opts: DbBackupOptions): Promise<void> {
  printPaperclipCliBanner();
  p.intro(pc.bgCyan(pc.black(" paperclip db:backup ")));

  const configPath = resolveConfigPath(opts.config);
  const config = readConfig(opts.config);
  const connection = resolveConnectionString(opts.config);
  const defaultDir = resolveDefaultBackupDir(resolvePaperclipInstanceId());
  const configuredDir = opts.dir?.trim() || config?.database.backup.dir || defaultDir;
  const backupDir = resolveBackupDir(configuredDir);

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Pass a positive integer: --retention-days 30.
  2. Fix config: set database.backup.retentionDays to an integer >= 1.
  3. If you want to keep all backups, pick a very large integer rather than 0 or negative.

Example fix

// before
--retention-days 0
// after
--retention-days 30
Defensive patterns

Strategy: validation

Validate before calling

function isValidRetentionDays(value: number | undefined, fallback: number): boolean {
  const candidate = value ?? fallback;
  return Number.isInteger(candidate) && candidate >= 1;
}

if (!isValidRetentionDays(opts.retentionDays, fallback)) {
  console.error('retention-days must be a positive integer >= 1.');
  process.exit(1);
}

Type guard

function isPositiveInteger(value: unknown): value is number {
  return typeof value === "number" && Number.isInteger(value) && value >= 1;
}

Prevention

When it happens

Trigger: Passing --retention-days 0, a negative number, a non-integer like 2.5, or having a config/database.backup.retentionDays that is misconfigured. The guard requires Number.isInteger AND >= 1.

Common situations: Env/config drift where retention was set to 0 to 'disable' pruning (this command does not support that), or a decimal days value from a calculation.

Related errors


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