jackwener/OpenCLI · error

Invalid trace maxAgeDays: ${maxAgeDays}

Error message

Invalid trace maxAgeDays: ${maxAgeDays}

What it means

resolveTraceRetentionPolicy() normalizes the trace retention policy and validates maxAgeDays before computing maxAgeMs. This Error is thrown when maxAgeDays (explicit or from the default policy) is not a finite number or is negative. It guarantees the resulting retention window is a sane, positive duration.

Source

Thrown at src/observation/retention.ts:61

  GB: 1024 ** 3,
};

export function parseByteSize(value: string | number): number {
  if (typeof value === 'number') {
    if (!Number.isFinite(value) || value < 0) throw new Error(`Invalid byte size: ${value}`);
    return Math.floor(value);
  }
  const match = value.trim().match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB)?$/i);
  if (!match) throw new Error(`Invalid byte size: ${value}`);
  const amount = Number(match[1]);
  const unit = (match[2] ?? 'B').toUpperCase();
  return Math.floor(amount * BYTES_UNITS[unit]);
}

export function resolveTraceRetentionPolicy(input: TraceRetentionPolicyInput = {}): ResolvedTraceRetentionPolicy {
  const maxAgeDays = input.maxAgeDays ?? DEFAULT_TRACE_RETENTION_POLICY.maxAgeDays;
  const maxCountPerProfile = input.maxCountPerProfile ?? DEFAULT_TRACE_RETENTION_POLICY.maxCountPerProfile;
  if (!Number.isFinite(maxAgeDays) || maxAgeDays < 0) throw new Error(`Invalid trace maxAgeDays: ${maxAgeDays}`);
  if (!Number.isInteger(maxCountPerProfile) || maxCountPerProfile < 0) {
    throw new Error(`Invalid trace maxCountPerProfile: ${maxCountPerProfile}`);
  }
  return {
    maxAgeDays,
    maxAgeMs: maxAgeDays * 24 * 60 * 60 * 1000,
    maxCountPerProfile,
    maxBytesPerProfile: parseByteSize(input.maxBytesPerProfile ?? DEFAULT_TRACE_RETENTION_POLICY.maxBytesPerProfile),
  };
}

export function traceExpiresAt(createdAt: string, policyInput: TraceRetentionPolicyInput = {}): string {
  const policy = resolveTraceRetentionPolicy(policyInput);
  const createdAtMs = Date.parse(createdAt);
  const base = Number.isFinite(createdAtMs) ? createdAtMs : Date.now();
  return new Date(base + policy.maxAgeMs).toISOString();
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Set maxAgeDays to a finite non-negative number (e.g. 30).
  2. Sanitize the input: Number(value) and check Number.isFinite before calling, defaulting on failure.
  3. Omit maxAgeDays entirely to use DEFAULT_TRACE_RETENTION_POLICY.maxAgeDays.

Example fix

// before
resolveTraceRetentionPolicy({ maxAgeDays: Number(process.env.TRACE_MAX_AGE_DAYS) }) // NaN if unset
// after
const days = Number(process.env.TRACE_MAX_AGE_DAYS);
resolveTraceRetentionPolicy({ maxAgeDays: Number.isFinite(days) && days >= 0 ? days : undefined })
Defensive patterns

Strategy: validation

Validate before calling

function isValidMaxAgeDays(v) {
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
}
// const days = Number(env.TRACE_MAX_AGE_DAYS);
// if (env.TRACE_MAX_AGE_DAYS !== undefined && !isValidMaxAgeDays(days)) throw new ConfigError(...);

Type guard

function isMaxAgeDays(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
}

Try / catch

try {
  return resolveTraceRetentionPolicy(input);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Invalid trace maxAgeDays:')) {
    logger.warn(`Bad maxAgeDays ${String(input?.maxAgeDays)}, falling back to defaults`);
    return resolveTraceRetentionPolicy({ ...input, maxAgeDays: undefined });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling resolveTraceRetentionPolicy({ maxAgeDays: -30 }) or { maxAgeDays: NaN } / Infinity; also any caller (e.g. policy()) passing a numeric string or null-adjacent value that bypasses types via `as any` or JS usage.

Common situations: Env var like TRACE_MAX_AGE_DAYS='-1', a NaN from Number(undefined) in a JS caller, or a config migration that left the field negative or missing in a way that yields NaN.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/6a90829d8e5acee0. Report an issue: GitHub.