abhigyanpatwari/GitNexus · error

list_repos: "${field}" must be an integer ${bound} (received

Error message

list_repos: "${field}" must be an integer ${bound} (received ${JSON.stringify(value)})

What it means

parseListReposPagination validates the list_repos tool's pagination params before use: limit and offset must be real numbers (not numeric strings), Number.isInteger, within bounds — limit between 1 and the backend's maxLimit, offset >= 0. On violation it throws a message that includes the field, the expected bound, and the JSON-stringified received value.

Source

Thrown at gitnexus/src/mcp/local/local-backend.ts:1136

 * non-number, `NaN`, non-integer, `limit < 1`, `limit > maxLimit`, or
 * `offset < 0` — are REJECTED with a clear error. `limit` is bounded but NOT
 * silently clamped: an over-max value throws (symmetric with the other bounds)
 * so a client never receives a smaller page than it asked for without knowing.
 * An omitted value (only `undefined`) falls back to the default.
 */
export function parseListReposPagination(
  params: { limit?: unknown; offset?: unknown } | null | undefined,
  opts: { defaultLimit: number; maxLimit: number },
): { limit: number; offset: number } {
  const requireInt = (value: unknown, field: string, min: number, max?: number): number => {
    const valid =
      typeof value === 'number' &&
      Number.isInteger(value) &&
      value >= min &&
      (max === undefined || value <= max);
    if (!valid) {
      const bound = max === undefined ? `>= ${min}` : `between ${min} and ${max}`;
      throw new Error(
        `list_repos: "${field}" must be an integer ${bound} (received ${JSON.stringify(value)})`,
      );
    }
    return value;
  };

  let limit = opts.defaultLimit;
  if (params?.limit !== undefined) {
    limit = requireInt(params.limit, 'limit', 1, opts.maxLimit);
  }

  let offset = 0;
  if (params?.offset !== undefined) {
    offset = requireInt(params.offset, 'offset', 0);
  }

  return { limit, offset };
}

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Send integer numbers within bounds: limit >= 1 and <= maxLimit, offset >= 0 — the error text names the exact bound you violated.
  2. Coerce and clamp client-side before calling: Number(...), Math.trunc, Math.min/Math.max.
  3. Omit the params entirely to receive the backend defaults (defaultLimit).
  4. If you genuinely need more rows than maxLimit, page through results with offset instead of raising limit.

Example fix

// before: string / out-of-range pagination
await callTool('list_repos', { limit: '50', offset: -1 });
await callTool('list_repos', { limit: 0 });

// after: validated integers, clamped before sending
const limit = Math.min(Math.max(Number.parseInt(rawLimit, 10) || 20, 1), maxLimit);
const offset = Math.max(Number.parseInt(rawOffset, 10) || 0, 0);
await callTool('list_repos', { limit, offset });
Defensive patterns

Strategy: validation

Validate before calling

// Clamp pagination to the backend contract before calling list_repos
const MAX_LIMIT = 100; // keep in sync with the backend's maxLimit

function normalizePagination(raw: { limit?: unknown; offset?: unknown }) {
  const limit = raw.limit === undefined ? undefined
    : Math.min(Math.max(Number.parseInt(String(raw.limit), 10), 1), MAX_LIMIT);
  const offset = raw.offset === undefined ? undefined
    : Math.max(Number.parseInt(String(raw.offset), 10), 0);
  if (Number.isNaN(limit) || Number.isNaN(offset)) throw new TypeError('pagination must be numeric');
  return { limit, offset };
}

Type guard

const isValidPagination = (p: unknown): p is { limit?: number; offset?: number } => {
  if (typeof p !== 'object' || p === null) return false;
  const { limit, offset } = p as { limit?: unknown; offset?: unknown };
  return (limit === undefined || (typeof limit === 'number' && Number.isInteger(limit) && limit >= 1)) &&
         (offset === undefined || (typeof offset === 'number' && Number.isInteger(offset) && offset >= 0));
};

Try / catch

try {
  return await backend.callTool('list_repos', params);
} catch (err) {
  if (err instanceof Error && err.message.includes('must be an integer')) {
    // echo the received value in the message back to the user for correction
    throw new UserInputError(err.message);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the MCP tool list_repos with limit: 0, a negative offset, a fractional limit like 10.5, a string such as "10" (sent by form-based or loosely typed clients), null, or a limit above the backend's configured maxLimit.

Common situations: Hand-written MCP clients that forward raw user input; JSON configs where pagination arrives as strings; SDK codegen that types params as any; test scripts poking edge values; client-side defaults computed from unclamped arithmetic (e.g. limit = total - 100 going to 0 or below).

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/df50f83ad127f918. Report an issue: GitHub.