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` pagination offset must be a finite, non-negative number. The tool floors the value and rejects NaN, ±Infinity, and negative numbers, since an invalid skip would corrupt result pagination for the underlying search.

Source

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

	constructor(private readonly session: ToolSession) {}

	async execute(
		_toolCallId: string,
		params: typeof astGrepSchema.infer,
		signal?: AbortSignal,
		_onUpdate?: AgentToolUpdateCallback<AstGrepToolDetails>,
		_context?: AgentToolContext,
	): Promise<AgentToolResult<AstGrepToolDetails>> {
		return untilAborted(signal, async () => {
			const pattern = params.pat.trim();
			if (pattern.length === 0) {
				throw new ToolError("`pat` must be a non-empty pattern");
			}
			const patterns = [pattern];
			const skip = params.skip === undefined ? 0 : Math.floor(params.skip);
			if (!Number.isFinite(skip) || skip < 0) {
				throw new ToolError("skip must be a non-negative number");
			}
			const scopedPaths = toPathList(params.path);
			const rawPaths = scopedPaths.length > 0 ? scopedPaths : ["."];
			const scope = await resolveToolSearchScope({
				rawPaths,
				cwd: this.session.cwd,
				internalUrlAction: "search",
				settings: this.session.settings,
				signal,
				sessionFile: this.session.getSessionFile() ?? undefined,
				localProtocolOptions: this.session.localProtocolOptions,
				skills: this.session.skills,
				resolveExternalUrl: async rawPath => {
					const target = parseReadUrlTarget(rawPath);
					if (!target) return undefined;
					const materialized = await materializeReadUrlToFile(
						this.session,
						{ path: target.path, raw: target.raw },

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass a non-negative integer for `skip` (or omit it to start at 0).
  2. Clamp the offset: skip = Math.max(0, Math.floor(Number(rawSkip) || 0)).
  3. Reject non-finite values at the caller boundary before invoking the tool.

Example fix

// before
await tool.execute({ pat, skip: page * -10 }, ctx);

// after
const skip = Math.max(0, Math.floor(page * 10));
await tool.execute({ pat, skip }, ctx);
Defensive patterns

Strategy: validation

Validate before calling

const skip = params.skip === undefined ? 0 : Math.floor(params.skip);
if (!Number.isFinite(skip) || skip < 0) throw new Error("skip must be a non-negative finite number");

Type guard

function isValidSkip(v: unknown): v is number {
  return typeof v === "number" && Number.isFinite(v) && v >= 0;
}

Try / catch

try {
  await tool.execute({ pat, skip }, ctx);
} catch (e) {
  if (e instanceof ToolError && e.message.includes("skip must be")) {
    // retry once with skip omitted (defaults to 0) or clamped value
  } else throw e;
}

Prevention

When it happens

Trigger: Calling AstGrepTool.execute with params.skip set to -1, NaN, Infinity, or -Infinity. (undefined is allowed and defaults to 0.)

Common situations: A model emits an out-of-range or symbolic value for `skip`; upstream code computes an offset that underflows (e.g. page * pageSize with a negative page); JSON parsing yields NaN from "NaN" input.

Related errors


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