can1357/oh-my-pi · error · ToolError

Skip must be a non-negative number

Error message

Skip must be a non-negative number

What it means

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.

Source

Thrown at packages/coding-agent/src/tools/grep.ts:967

		params: SearchParams,
		signal?: AbortSignal,
		_onUpdate?: AgentToolUpdateCallback<GrepToolDetails>,
		_toolContext?: AgentToolContext,
	): Promise<AgentToolResult<GrepToolDetails>> {
		const { pattern, path: rawPath, case: caseSensitive, gitignore, skip } = params;

		return untilAborted(signal, async () => {
			// Preserve the pattern verbatim — leading/trailing whitespace is
			// meaningful in regexes (indentation anchors, trailing-space matches).
			if (!pattern.trim()) {
				throw new ToolError("Pattern must not be empty");
			}
			const normalizedPattern = pattern;

			const normalizedSkip =
				skip === undefined || skip === null ? 0 : Number.isFinite(skip) ? Math.floor(skip) : Number.NaN;
			if (normalizedSkip < 0 || !Number.isFinite(normalizedSkip)) {
				throw new ToolError("Skip must be a non-negative number");
			}
			const scopedPaths = toPathList(rawPath);
			const effectivePaths = scopedPaths.length > 0 ? scopedPaths : ["."];
			const rawEntries = await expandDelimitedPathEntries(effectivePaths, this.session.cwd);
			const pathSpecs = await parsePathSpecs(rawEntries, this.session.cwd);
			const materializedExternalPaths = new Map<string, string>();
			const materializeExternalUrlForSearch = async (rawPath: string) => {
				const target = parseReadUrlTarget(rawPath);
				if (!target) return undefined;
				const materialized = await materializeReadUrlToFile(
					this.session,
					{ path: target.path, raw: target.raw },
					signal,
				);
				materializedExternalPaths.set(rawPath, materialized.path);
				return { sourcePath: materialized.path, immutable: true };
			};
			const {

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass a non-negative integer (0 for the first page)
  2. Sanitize user input: const s = Math.max(0, Math.floor(Number(raw) || 0))
  3. Check the page calculation that produced the negative value (e.g. page 0 minus 1)

Example fix

// before
await grep({ pattern: "err", skip: (page - 1) * pageSize });
// after
const skip = Math.max(0, (page - 1) * pageSize);
await grep({ pattern: "err", skip });
Defensive patterns

Strategy: validation

Validate before calling

const skip = Math.max(0, Math.floor(Number(rawSkip) || 0));
if (!Number.isFinite(skip) || skip < 0) throw new Error("skip must be a non-negative number");

Type guard

const isNonNegativeFinite = (n: unknown): n is number => typeof n === "number" && Number.isFinite(n) && n >= 0;

Prevention

When it happens

Trigger: grep with skip: -5, skip: "10" coerced to NaN, skip: Number("") (0 would pass but Number("abc") yields NaN), or skip: Infinity.

Common situations: 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.

Understand the failure class

Background: "must be positive", "Invalid value": how libraries reject invalid parameter values (ValueError, ArgumentError, INVALID_PARAMETER_VALUE) — this error's family across 28 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/2d7ed40340ec99db. Report an issue: GitHub.