{"record":{"id":"ca94e6433a25fb25","repo":"tinyhumansai/openhuman","slug":"invalid-paramname-value-must-be-a-positive","errorCode":null,"errorMessage":"Invalid ${paramName}: ${value}. Must be a positive integer.","messagePattern":"Invalid (.+?): (.+?)\\. Must be a positive integer\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"app/src/lib/mcp/validation.ts","lineNumber":78,"sourceCode":"    try {\n      return validateId(item, `${paramName}[${index}]`);\n    } catch (error) {\n      if (error instanceof ValidationError) {\n        throw error;\n      }\n      const errorMsg = error instanceof Error ? error.message : String(error);\n      throw new ValidationError(`Invalid ${paramName}[${index}]: ${errorMsg}`);\n    }\n  });\n}\n\n/**\n * Validate a positive integer parameter (e.g. message IDs)\n */\nexport function validatePositiveInt(value: unknown, paramName: string): number {\n  if (typeof value === 'number') {\n    if (!Number.isInteger(value) || value <= 0) {\n      throw new ValidationError(`Invalid ${paramName}: ${value}. Must be a positive integer.`);\n    }\n    return value;\n  }\n\n  if (typeof value === 'string') {\n    const intValue = Number.parseInt(value, 10);\n    if (Number.isNaN(intValue) || intValue <= 0) {\n      throw new ValidationError(`Invalid ${paramName}: '${value}'. Must be a positive integer.`);\n    }\n    return intValue;\n  }\n\n  throw new ValidationError(`Invalid ${paramName}: ${String(value)}. Must be a positive integer.`);\n}\n\n/**\n * Validate optional ID (can be undefined)\n */","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/lib/mcp/validation.ts#L60-L96","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst params = { message_id: cursor ?? 0 };\nawait mcpCall(params);\n\n// after\nconst params = { message_id: Math.max(1, Math.trunc(cursor ?? 1)) };\nawait mcpCall(params);","handlingStrategy":"validation","validationCode":"function toPositiveInt(v: unknown): number | undefined {\n  const n = typeof v === 'number' ? v : Number(v);\n  return Number.isInteger(n) && n > 0 ? n : undefined;\n}\nconst id = toPositiveInt(params.message_id);\nif (id === undefined) throw new UserInputError('message_id must be a positive integer');","typeGuard":"function isPositiveInt(v: unknown): v is number {\n  return typeof v === 'number' && Number.isInteger(v) && v > 0;\n}","tryCatchPattern":"try {\n  validatePositiveInt(value, 'message_id');\n} catch (err) {\n  if (err instanceof ValidationError) return badRequest(err.message);\n  throw err;\n}","preventionTips":["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."],"tags":["mcp","validation","integer","parameters","input"],"backgroundTag":"invalid-parameter-type","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}