{"record":{"id":"3bc606eee6d05100","repo":"jackwener/OpenCLI","slug":"invalid-trace-maxcountperprofile-maxcountperpro","errorCode":null,"errorMessage":"Invalid trace maxCountPerProfile: ${maxCountPerProfile}","messagePattern":"Invalid trace maxCountPerProfile: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/observation/retention.ts","lineNumber":63,"sourceCode":"\nexport function parseByteSize(value: string | number): number {\n  if (typeof value === 'number') {\n    if (!Number.isFinite(value) || value < 0) throw new Error(`Invalid byte size: ${value}`);\n    return Math.floor(value);\n  }\n  const match = value.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*(B|KB|MB|GB)?$/i);\n  if (!match) throw new Error(`Invalid byte size: ${value}`);\n  const amount = Number(match[1]);\n  const unit = (match[2] ?? 'B').toUpperCase();\n  return Math.floor(amount * BYTES_UNITS[unit]);\n}\n\nexport function resolveTraceRetentionPolicy(input: TraceRetentionPolicyInput = {}): ResolvedTraceRetentionPolicy {\n  const maxAgeDays = input.maxAgeDays ?? DEFAULT_TRACE_RETENTION_POLICY.maxAgeDays;\n  const maxCountPerProfile = input.maxCountPerProfile ?? DEFAULT_TRACE_RETENTION_POLICY.maxCountPerProfile;\n  if (!Number.isFinite(maxAgeDays) || maxAgeDays < 0) throw new Error(`Invalid trace maxAgeDays: ${maxAgeDays}`);\n  if (!Number.isInteger(maxCountPerProfile) || maxCountPerProfile < 0) {\n    throw new Error(`Invalid trace maxCountPerProfile: ${maxCountPerProfile}`);\n  }\n  return {\n    maxAgeDays,\n    maxAgeMs: maxAgeDays * 24 * 60 * 60 * 1000,\n    maxCountPerProfile,\n    maxBytesPerProfile: parseByteSize(input.maxBytesPerProfile ?? DEFAULT_TRACE_RETENTION_POLICY.maxBytesPerProfile),\n  };\n}\n\nexport function traceExpiresAt(createdAt: string, policyInput: TraceRetentionPolicyInput = {}): string {\n  const policy = resolveTraceRetentionPolicy(policyInput);\n  const createdAtMs = Date.parse(createdAt);\n  const base = Number.isFinite(createdAtMs) ? createdAtMs : Date.now();\n  return new Date(base + policy.maxAgeMs).toISOString();\n}\n\nexport function pruneTraceArtifacts(\n  tracesDir: string,","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/observation/retention.ts#L45-L81","documentation":"resolveTraceRetentionPolicy() requires maxCountPerProfile to be a non-negative integer because it caps how many traces are kept per profile. This Error is thrown when the value is fractional (e.g. 10.5), negative, or otherwise not an integer. Complements the maxAgeDays check with the same fail-fast policy.","triggerScenarios":"Calling resolveTraceRetentionPolicy({ maxCountPerProfile: -5 }) or { maxCountPerProfile: 100.5 }; also NaN/Infinity which fail Number.isInteger.","commonSituations":"Config values divided or averaged at runtime (e.g. total/profiles producing fractions), a negative from a bad formula, or a percent-like value ('50%') parsed loosely in JS.","solutions":["Round the value: Math.floor/Math.round to a non-negative integer before passing it.","Fix the upstream calculation or config so the count is a whole non-negative number.","Omit maxCountPerProfile to fall back to DEFAULT_TRACE_RETENTION_POLICY.maxCountPerProfile."],"exampleFix":"// before\nresolveTraceRetentionPolicy({ maxCountPerProfile: totalTraces / profileCount }) // may be fractional\n// after\nresolveTraceRetentionPolicy({ maxCountPerProfile: Math.max(0, Math.floor(totalTraces / profileCount)) })","handlingStrategy":"validation","validationCode":"function isValidMaxCountPerProfile(v) {\n  return typeof v === 'number' && Number.isInteger(v) && v >= 0;\n}\n// if (!isValidMaxCountPerProfile(cfg.maxCountPerProfile)) cfg.maxCountPerProfile = undefined; // use default","typeGuard":"function isMaxCountPerProfile(v: unknown): v is number {\n  return typeof v === 'number' && Number.isInteger(v) && v >= 0;\n}","tryCatchPattern":"try {\n  return resolveTraceRetentionPolicy(input);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('maxCountPerProfile')) {\n    logger.warn(`Bad maxCountPerProfile ${String(input?.maxCountPerProfile)}, falling back to defaults`);\n    return resolveTraceRetentionPolicy({ ...input, maxCountPerProfile: undefined });\n  }\n  throw e;\n}","preventionTips":["Math.floor/round any computed counts before passing them in.","Schema-validate with z.number().int().nonnegative() at config load.","Never pass user-supplied strings or percentages; parse and validate to an integer first."],"tags":["validation","configuration","retention"],"backgroundTag":"invalid-config-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}