{"record":{"id":"337dacc2e9570df4","repo":"nexu-io/open-design","slug":"field-must-be-a-positive-integer","errorCode":null,"errorMessage":"${field} must be a positive integer","messagePattern":"(.+?) must be a positive integer","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/refresh.ts","lineNumber":520,"sourceCode":"        });\n    deepMergeBoundedJsonObject(dataJson, mapped);\n    if (source.toolName === 'public_github_repository_metric') {\n      applyLegacyGithubRepositoryMetricCompat(dataJson, options.documentOutput.output);\n    }\n  }\n\n  return { dataJson: asBoundedRefreshOutput(dataJson) };\n}\n\nfunction optionalString(value: BoundedJsonValue | undefined, field: string): string | undefined {\n  if (value === undefined) return undefined;\n  if (typeof value !== 'string') throw new Error(`${field} must be a string`);\n  return value;\n}\n\nfunction optionalPositiveInteger(value: BoundedJsonValue | undefined, field: string, defaultValue: number, maxValue: number): number {\n  if (value === undefined) return defaultValue;\n  if (!Number.isSafeInteger(value) || typeof value !== 'number' || value < 1) throw new Error(`${field} must be a positive integer`);\n  return Math.min(value, maxValue);\n}\n\nfunction selectJsonPath(input: ProjectFilesReadJsonInput): string {\n  const rawPath = optionalString(input.path, 'input.path') ?? optionalString(input.file, 'input.file') ?? optionalString(input.name, 'input.name');\n  if (rawPath === undefined) throw new Error('project_files.read_json requires input.path');\n  return validateProjectPath(rawPath);\n}\n\nfunction compactTextPreview(text: string, query: string | undefined): string {\n  const normalized = text.replace(/\\s+/g, ' ').trim();\n  if (normalized.length <= 240) return normalized;\n  if (query === undefined || query.trim().length === 0) return `${normalized.slice(0, 240)}…`;\n  const index = normalized.toLowerCase().indexOf(query.toLowerCase());\n  if (index < 0) return `${normalized.slice(0, 240)}…`;\n  const start = Math.max(0, index - 80);\n  return `${start > 0 ? '…' : ''}${normalized.slice(start, start + 240)}…`;\n}","sourceCodeStart":502,"sourceCodeEnd":538,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/refresh.ts#L502-L538","documentation":"optionalPositiveInteger (refresh.ts:518-522) validates numeric options like git.summary's input.maxCommits. It throws if the value is present but is not a safe integer, not a number type, or is less than 1. Valid values are clamped to maxValue (e.g. 50 for maxCommits).","triggerScenarios":"input.maxCommits is 0, negative, a float (e.g. 2.5), NaN, or a non-number (string/object).","commonSituations":"User-supplied maxCommits of 0 meaning 'none'; a float passed by mistake; a string like '10' that was not coerced; a config default of -1 used as a sentinel.","solutions":["Omit maxCommits to use the default (10), or set it to an integer between 1 and 50.","Coerce/parseInt user input and reject values < 1 before refresh.","If you need 'no commits', leave the field unset rather than passing 0."],"exampleFix":"// before\nconst input = { maxCommits: 0 };\n// throws `input.maxCommits must be a positive integer`\n\n// after: omit or use a valid positive integer\nconst input = { maxCommits: 10 };","handlingStrategy":"validation","validationCode":"function optionalPositiveInteger(v: unknown, field: string, def: number, max: number): number {\n  if (v === undefined) return def;\n  if (typeof v !== 'number' || !Number.isSafeInteger(v) || v < 1) {\n    throw new Error(`${field} must be a positive integer`);\n  }\n  return Math.min(v, max);\n}","typeGuard":"function isPositiveInteger(v: unknown): v is number {\n  return typeof v === 'number' && Number.isSafeInteger(v) && v >= 1;\n}","tryCatchPattern":null,"preventionTips":["Omit numeric options to use defaults rather than passing 0 as a sentinel.","Coerce with Math.trunc/parseInt and validate >= 1 at the input boundary.","Document the maxValue clamp (e.g. maxCommits is clamped to 50) for tool inputs."],"tags":["live-artifacts","refresh","input-validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}