tinyhumansai/openhuman · error · ValidationError
Invalid ${paramName}: ${value}. Must be a positive integer.
Error message
Invalid ${paramName}: ${value}. Must be a positive integer. What it means
validatePositiveInt() in the MCP validation layer checks that a parameter is a positive integer (e.g. message IDs). When the value is a JS number but either not an integer (fractional/Infinity/NaN) or <= 0, it throws ValidationError 'Invalid <param>: <value>. Must be a positive integer.' — the numeric-input branch of the guard.
Source
Thrown at app/src/lib/mcp/validation.ts:78
try {
return validateId(item, `${paramName}[${index}]`);
} catch (error) {
if (error instanceof ValidationError) {
throw error;
}
const errorMsg = error instanceof Error ? error.message : String(error);
throw new ValidationError(`Invalid ${paramName}[${index}]: ${errorMsg}`);
}
});
}
/**
* Validate a positive integer parameter (e.g. message IDs)
*/
export function validatePositiveInt(value: unknown, paramName: string): number {
if (typeof value === 'number') {
if (!Number.isInteger(value) || value <= 0) {
throw new ValidationError(`Invalid ${paramName}: ${value}. Must be a positive integer.`);
}
return value;
}
if (typeof value === 'string') {
const intValue = Number.parseInt(value, 10);
if (Number.isNaN(intValue) || intValue <= 0) {
throw new ValidationError(`Invalid ${paramName}: '${value}'. Must be a positive integer.`);
}
return intValue;
}
throw new ValidationError(`Invalid ${paramName}: ${String(value)}. Must be a positive integer.`);
}
/**
* Validate optional ID (can be undefined)
*/View on GitHub (pinned to 7491200858)
Solutions
- Fix the caller to send a genuine positive integer (>= 1) for the param named in the message.
- Clamp/normalize computed values: Math.max(1, Math.trunc(value)) before invoking.
- If 0 should be meaningful, the API needs a different validator (validateOptionalId / non-negative variant) — file it upstream rather than bypassing.
- Add schema validation at the tool boundary (JSON Schema type integer, minimum 1) to reject early with clearer errors.
Example fix
// before
const params = { message_id: cursor ?? 0 };
await mcpCall(params);
// after
const params = { message_id: Math.max(1, Math.trunc(cursor ?? 1)) };
await mcpCall(params); Defensive patterns
Strategy: validation
Validate before calling
function toPositiveInt(v: unknown): number | undefined {
const n = typeof v === 'number' ? v : Number(v);
return Number.isInteger(n) && n > 0 ? n : undefined;
}
const id = toPositiveInt(params.message_id);
if (id === undefined) throw new UserInputError('message_id must be a positive integer'); Type guard
function isPositiveInt(v: unknown): v is number {
return typeof v === 'number' && Number.isInteger(v) && v > 0;
} Try / catch
try {
validatePositiveInt(value, 'message_id');
} catch (err) {
if (err instanceof ValidationError) return badRequest(err.message);
throw err;
} Prevention
- Type tool params with TS interfaces (id: number) so compilers catch bad shapes.
- Clamp computed cursors with Math.max(1, Math.trunc(n)).
- Validate at the form/UI layer before hitting MCP boundaries.
When it happens
Trigger: Calling an MCP tool/resource handler with params like message_id: 0, cursor: -5, limit: 2.5, or NaN (often from parseInt/parseFloat upstream or JSON with wrong types). The number branch fails before string coercion is attempted.
Common situations: Tool callers sending 0 as a default value; computed cursors going negative on empty results; float math leaking into IDs; deserialized JSON where the field was expected int but is float.
Related errors
- Invalid ${paramName}: '${value}'. Must be a positive integer
- Invalid ${paramName}: ${String(value)}. Must be a positive i
- Recovery phrase is required.
- "{key}" is required
- mascot manifest: missing mascots array
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/ca94e6433a25fb25.
Report an issue: GitHub.