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
- Set maxAgeDays to a finite non-negative number (e.g. 30).
- Sanitize the input: Number(value) and check Number.isFinite before calling, defaulting on failure.
- 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
- Coerce env-var numbers with Number() and Number.isFinite before use; treat undefined as 'use default'.
- Add zod/yup schema: z.number().finite().nonnegative() for maxAgeDays at config load time.
- Avoid arithmetic that can yield NaN (e.g. days * MULTIPLIER where days is a string) before passing the value.
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
- Invalid trace maxCountPerProfile: ${maxCountPerProfile}
- profile contextId is required
- profile alias is required
- profile is required
- TAP_MISSING_PARAMS
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/6a90829d8e5acee0.
Report an issue: GitHub.