abhigyanpatwari/GitNexus · error
${source} must be a positive integer.
Error message
${source} must be a positive integer. What it means
Thrown by parsePositiveInteger in the MCP output-budget module when a token budget value is not a usable positive integer. It guards the maxTokens argument accepted by the budgeted MCP tools (query, context, impact) and the GITNEXUS_MCP_DEFAULT_MAX_TOKENS environment variable. A value passes only if it is a number that is a safe integer > 0, or a string matching ^[1-9]\d*$ that parses to a safe integer (whitespace is trimmed first). Anything else — 0, negatives, floats, '1e3', 'abc', '007', values above Number.MAX_SAFE_INTEGER — throws immediately, before any tool backend runs.
Source
Thrown at gitnexus/src/mcp/output-budget.ts:12
const BUDGETED_TOOLS = new Set(['query', 'context', 'impact']);
export const MCP_TOKEN_ESTIMATE_BYTES = 4;
export const MCP_TRUNCATION_MARKER = '\n…';
function parsePositiveInteger(value: unknown, source: string): number {
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) return value;
if (typeof value === 'string' && /^[1-9]\d*$/.test(value.trim())) {
const parsed = Number(value.trim());
if (Number.isSafeInteger(parsed)) return parsed;
}
throw new Error(`${source} must be a positive integer.`);
}
export function resolveMcpMaxTokens(
toolName: string,
args: Record<string, unknown> | undefined,
env: NodeJS.ProcessEnv = process.env,
): number | undefined {
if (!BUDGETED_TOOLS.has(toolName)) return undefined;
if (args?.maxTokens !== undefined) return parsePositiveInteger(args.maxTokens, 'maxTokens');
const configured = env.GITNEXUS_MCP_DEFAULT_MAX_TOKENS;
if (configured === undefined || configured.trim() === '') return undefined;
return parsePositiveInteger(configured, 'GITNEXUS_MCP_DEFAULT_MAX_TOKENS');
}
function utf8Prefix(text: string, maxBytes: number): string {
let bytes = 0;
const codePoints: string[] = [];View on GitHub (pinned to aac7515d2a)
Solutions
- Pass maxTokens as a plain positive integer (number or digit-only string), e.g. 4096 — no decimals, no scientific notation, no leading zeros, and at most Number.MAX_SAFE_INTEGER.
- If you do not want a budget, omit the maxTokens argument entirely instead of passing 0.
- Check and fix GITNEXUS_MCP_DEFAULT_MAX_TOKENS in the server environment (unset it or set it to a plain positive integer like 4096).
- If the value comes from another system, coerce and validate it client-side before the MCP call (Number.isSafeInteger check).
Example fix
// before
const out = await callTool('query', { search_query: 'auth flow', maxTokens: tokens }); // tokens = 0 or 4096.5
// after
const tokens = Number.isSafeInteger(raw) && raw > 0 ? raw : 4096;
const out = await callTool('query', { search_query: 'auth flow', maxTokens: tokens }); Defensive patterns
Strategy: validation
Validate before calling
function toMaxTokens(v: unknown): number | undefined {
if (v === undefined) return undefined;
if (typeof v === 'number' && Number.isSafeInteger(v) && v > 0) return v;
if (typeof v === 'string' && /^[1-9]\d*$/.test(v.trim())) {
const n = Number(v.trim());
if (Number.isSafeInteger(n)) return n;
}
return undefined; // omit the arg rather than send an invalid one
}
const args = { search_query: 'auth' } as Record<string, unknown>;
const mt = toMaxTokens(userBudget);
if (mt !== undefined) args.maxTokens = mt; Type guard
const isPositiveIntArg = (v: unknown): v is number | string => (typeof v === 'number' && Number.isSafeInteger(v) && v > 0) || (typeof v === 'string' && /^[1-9]\d*$/.test(v.trim()) && Number.isSafeInteger(Number(v.trim())));
Try / catch
try {
await client.callTool({ name: 'query', arguments });
} catch (e) {
if (e instanceof Error && /must be a positive integer/.test(e.message)) {
// deterministic input error: fix the value, do not retry
throw new Error(`Bad maxTokens: ${JSON.stringify(arguments['maxTokens'])}`);
}
throw e;
} Prevention
- Never derive maxTokens with division unless you Math.floor and clamp to >= 1.
- Treat 0 as 'omit the budget' client-side; the server rejects it.
- Check GITNEXUS_MCP_DEFAULT_MAX_TOKENS in the server environment with the same regex before deploying.
When it happens
Trigger: Calling the MCP 'query', 'context', or 'impact' tool with maxTokens: 0, maxTokens: -100, maxTokens: 5000.5, maxTokens: '1e3', or maxTokens: 'abc'. Or starting/serving MCP with env GITNEXUS_MCP_DEFAULT_MAX_TOKENS='-1', '10.5', 'true', or '1e4' while any budgeted tool is invoked without an explicit maxTokens argument.
Common situations: An AI client computes maxTokens from a model context size and passes 0 or a float (e.g. ctx.window / 1000). A .env file sets GITNEXUS_MCP_DEFAULT_MAX_TOKENS with quotes or units ('4096 tokens', '"4096"'). Copying a permissive validator that accepts '1e3' style scientific notation, which the strict ^[1-9]\d*$ regex deliberately rejects.
Related errors
- GITNEXUS_MCP_READ_ONLY must be 0 or 1.
- ${allowedRaw.key} must not be blank.
- ${defaultRaw.key} must not be blank.
- ${name} must be a positive integer, got "${raw}"
- ${name} must be a non-negative integer, got "${raw}"
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/5bb3a536d19b3e39.
Report an issue: GitHub.