{"record":{"id":"f2e78700c0c678f5","repo":"can1357/oh-my-pi","slug":"lookbackdays-must-be-a-positive-integer-or-all","errorCode":null,"errorMessage":"lookbackDays must be a positive integer or 'all'","messagePattern":"lookbackDays must be a positive integer or 'all'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/security/cloud.ts","lineNumber":271,"sourceCode":"\n\tasync getConfiguration(configurationId: string, signal?: AbortSignal): Promise<CodexSecurityCloudConfiguration> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst page = await this.listConfigurations({ limit: 500, cursor, signal });\n\t\t\tconst found = page.items.find(item => item.id === configurationId || item.sourceId === configurationId);\n\t\t\tif (found) return found;\n\t\t\tcursor = page.nextCursor;\n\t\t} while (cursor);\n\t\tthrow new Error(`Unknown Codex Security cloud configuration: ${configurationId}`);\n\t}\n\n\tasync startScan(input: StartCodexSecurityCloudScanInput): Promise<CodexSecurityCloudConfiguration> {\n\t\tif (\n\t\t\tinput.lookbackDays !== undefined &&\n\t\t\tinput.lookbackDays !== \"all\" &&\n\t\t\t(!Number.isInteger(input.lookbackDays) || input.lookbackDays < 1)\n\t\t) {\n\t\t\tthrow new Error(\"lookbackDays must be a positive integer or 'all'\");\n\t\t}\n\t\tconst raw = await this.#request(\"scan_configurations\", {\n\t\t\tmethod: \"POST\",\n\t\t\tsignal: input.signal,\n\t\t\tbody: accessToken => {\n\t\t\t\tconst scanInput: JsonObject = {\n\t\t\t\t\tenvironment_id: input.environmentId,\n\t\t\t\t\tlookback_days: input.lookbackDays === \"all\" ? null : (input.lookbackDays ?? 30),\n\t\t\t\t\tnotification_rules: [],\n\t\t\t\t\towner_id: jwtSubject(accessToken),\n\t\t\t\t\trepo_id: input.repositoryId,\n\t\t\t\t\trepo_url: input.repositoryUrl,\n\t\t\t\t\tshare_targets: [],\n\t\t\t\t\tstate: \"enabled\",\n\t\t\t\t};\n\t\t\t\tif (input.maintainerAttackConcerns) scanInput.maintainer_attack_concerns = input.maintainerAttackConcerns;\n\t\t\t\tif (input.maintainerFocusAreas) scanInput.maintainer_focus_areas = input.maintainerFocusAreas;\n\t\t\t\tif (input.maintainerAdditionalContext)","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/security/cloud.ts#L253-L289","documentation":"startScan validates the optional lookbackDays field of StartCodexSecurityCloudScanInput before creating a cloud scan configuration. The value must be a positive integer (>= 1) or the literal string 'all'; undefined (omit) is also allowed. Any other number — fractional, zero, or negative — is rejected client-side so an invalid request never reaches the cloud API.","triggerScenarios":"Calling client.startScan({ ... }) with lookbackDays set to 0, a negative number, a float like 7.5, or any non-'all' non-integer value.","commonSituations":"Computing lookback days via date math that yields a fractional value (e.g. ms-diff / DAY_MS without rounding); passing a config default of 0 meaning 'no limit' instead of 'all'; hand-editing a JSON config where the field becomes a string like '30'.","solutions":["Pass a positive integer: lookbackDays: 30","Use the string 'all' to scan full history: lookbackDays: 'all'","Omit the field entirely (undefined) to use the cloud-side default","Round computed values before calling: Math.max(1, Math.floor(computedDays))"],"exampleFix":"// before\nawait client.startScan({ lookbackDays: (Date.now() - since) / 86400000 });\n// after\nconst days = Math.max(1, Math.round((Date.now() - since) / 86400000));\nawait client.startScan({ lookbackDays: days });","handlingStrategy":"validation","validationCode":"function isValidLookback(v: unknown): v is number | \"all\" | undefined {\n\treturn v === undefined || v === \"all\" || (Number.isInteger(v) && (v as number) >= 1);\n}\nif (!isValidLookback(input.lookbackDays)) throw new TypeError(\"lookbackDays must be a positive integer or 'all'\");","typeGuard":"const isLookback = (v: unknown): v is number | \"all\" =>\n\tv === \"all\" || (typeof v === \"number\" && Number.isInteger(v) && v >= 1);","tryCatchPattern":"try {\n\tawait client.startScan(input);\n} catch (err) {\n\tif (err instanceof Error && err.message.includes(\"lookbackDays\")) {\n\t\tinput.lookbackDays = \"all\"; // or surface a user-facing config error\n\t} else throw err;\n}","preventionTips":["Round any computed day counts: Math.max(1, Math.round(days))","Use 'all' explicitly for unlimited lookback instead of 0","Type the field as number | 'all' at your config boundary and validate on load","Unit-test config parsing for fractional/zero lookback values"],"tags":["validation","api-input","security-scan"],"backgroundTag":"invalid-parameter-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}