{"record":{"id":"2d7ed40340ec99db","repo":"can1357/oh-my-pi","slug":"skip-must-be-a-non-negative-number-2d7ed4","errorCode":null,"errorMessage":"Skip must be a non-negative number","messagePattern":"Skip must be a non-negative number","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/grep.ts","lineNumber":967,"sourceCode":"\t\tparams: SearchParams,\n\t\tsignal?: AbortSignal,\n\t\t_onUpdate?: AgentToolUpdateCallback<GrepToolDetails>,\n\t\t_toolContext?: AgentToolContext,\n\t): Promise<AgentToolResult<GrepToolDetails>> {\n\t\tconst { pattern, path: rawPath, case: caseSensitive, gitignore, skip } = params;\n\n\t\treturn untilAborted(signal, async () => {\n\t\t\t// Preserve the pattern verbatim — leading/trailing whitespace is\n\t\t\t// meaningful in regexes (indentation anchors, trailing-space matches).\n\t\t\tif (!pattern.trim()) {\n\t\t\t\tthrow new ToolError(\"Pattern must not be empty\");\n\t\t\t}\n\t\t\tconst normalizedPattern = pattern;\n\n\t\t\tconst normalizedSkip =\n\t\t\t\tskip === undefined || skip === null ? 0 : Number.isFinite(skip) ? Math.floor(skip) : Number.NaN;\n\t\t\tif (normalizedSkip < 0 || !Number.isFinite(normalizedSkip)) {\n\t\t\t\tthrow new ToolError(\"Skip must be a non-negative number\");\n\t\t\t}\n\t\t\tconst scopedPaths = toPathList(rawPath);\n\t\t\tconst effectivePaths = scopedPaths.length > 0 ? scopedPaths : [\".\"];\n\t\t\tconst rawEntries = await expandDelimitedPathEntries(effectivePaths, this.session.cwd);\n\t\t\tconst pathSpecs = await parsePathSpecs(rawEntries, this.session.cwd);\n\t\t\tconst materializedExternalPaths = new Map<string, string>();\n\t\t\tconst materializeExternalUrlForSearch = async (rawPath: string) => {\n\t\t\t\tconst target = parseReadUrlTarget(rawPath);\n\t\t\t\tif (!target) return undefined;\n\t\t\t\tconst materialized = await materializeReadUrlToFile(\n\t\t\t\t\tthis.session,\n\t\t\t\t\t{ path: target.path, raw: target.raw },\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t\tmaterializedExternalPaths.set(rawPath, materialized.path);\n\t\t\t\treturn { sourcePath: materialized.path, immutable: true };\n\t\t\t};\n\t\t\tconst {","sourceCodeStart":949,"sourceCodeEnd":985,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/grep.ts#L949-L985","documentation":"The `skip` option (number of leading matches to skip for pagination) must be a non-negative finite number. undefined/null are treated as 0, but negative values, non-numeric values, and NaN (e.g. from a non-numeric string converted via Number) are rejected.","triggerScenarios":"grep with skip: -5, skip: \"10\" coerced to NaN, skip: Number(\"\") (0 would pass but Number(\"abc\") yields NaN), or skip: Infinity.","commonSituations":"Paginating results with an off-by-one negative page index; parsing a page param from a query string that is empty or non-numeric; passing a string instead of a number.","solutions":["Pass a non-negative integer (0 for the first page)","Sanitize user input: const s = Math.max(0, Math.floor(Number(raw) || 0))","Check the page calculation that produced the negative value (e.g. page 0 minus 1)"],"exampleFix":"// before\nawait grep({ pattern: \"err\", skip: (page - 1) * pageSize });\n// after\nconst skip = Math.max(0, (page - 1) * pageSize);\nawait grep({ pattern: \"err\", skip });","handlingStrategy":"validation","validationCode":"const skip = Math.max(0, Math.floor(Number(rawSkip) || 0));\nif (!Number.isFinite(skip) || skip < 0) throw new Error(\"skip must be a non-negative number\");","typeGuard":"const isNonNegativeFinite = (n: unknown): n is number => typeof n === \"number\" && Number.isFinite(n) && n >= 0;","tryCatchPattern":null,"preventionTips":["Clamp pagination math with Math.max(0, ...)","Coerce and validate string inputs with Number() before passing"],"tags":["grep","validation","pagination"],"backgroundTag":"invalid-parameter-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}