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
- Send integer numbers within bounds: limit >= 1 and <= maxLimit, offset >= 0 — the error text names the exact bound you violated.
- Coerce and clamp client-side before calling: Number(...), Math.trunc, Math.min/Math.max.
- Omit the params entirely to receive the backend defaults (defaultLimit).
- 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
- Coerce form/env input with parseInt + clamp before forwarding to MCP tools.
- Parse the error's JSON.stringify-ed received value to show users exactly what was wrong.
- Omit optional pagination fields rather than sending 0/'' defaults.
- Page with offset instead of inflating limit past maxLimit.
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
- Path traversal blocked: ${filePath}
- Missing Content-Length header from MCP client
- Invalid Content-Length header from MCP client
- Content-Length ${contentLength} exceeds maximum allowed size
- Refusing to start the MCP HTTP server on a non-loopback host
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/df50f83ad127f918.
Report an issue: GitHub.