{"record":{"id":"2a368c2dc07510ea","repo":"tinyhumansai/openhuman","slug":"agentteamapi-label-must-be-a-positive-integer","errorCode":null,"errorMessage":"agentTeamApi: ${label} must be a positive integer","messagePattern":"agentTeamApi: (.+?) must be a positive integer","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/api/agentTeamApi.ts","lineNumber":160,"sourceCode":"interface RawRunEvent {\n  runId: string;\n  sequence: number;\n  eventType: string;\n  payload: unknown;\n  timestamp: string;\n}\n\n/** Optional filters for {@link agentTeamApi.list}. Mirrors `AgentTeamListRequest`. */\ninterface AgentTeamListParams {\n  parentThreadId?: string;\n  status?: AgentTeamStatus;\n  limit?: number;\n  offset?: number;\n}\n\nfunction assertPositiveInt(value: number | undefined, label: string): void {\n  if (value !== undefined && (!Number.isInteger(value) || value <= 0)) {\n    throw new Error(`agentTeamApi: ${label} must be a positive integer`);\n  }\n}\n\n/** Coerce a raw run-event payload into a typed message payload, defensively. */\nfunction readMessagePayload(payload: unknown): TeamMessagePayload {\n  const p = (payload ?? {}) as Record<string, unknown>;\n  return {\n    from: typeof p.from === 'string' ? p.from : '',\n    to: typeof p.to === 'string' ? p.to : null,\n    content: typeof p.content === 'string' ? p.content : '',\n    visibility: typeof p.visibility === 'string' ? p.visibility : 'team',\n  };\n}\n\nexport const agentTeamApi = {\n  /**\n   * List team headers, newest first. Filters are optional; `parentThreadId`\n   * scopes to one conversation, `status` to active/closed.","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/agentTeamApi.ts#L142-L178","documentation":"Client-side pre-flight validation in agentTeamApi. The helper assertPositiveInt rejects any explicitly-supplied pagination value that is not an integer greater than zero, before the JSON-RPC call to the core is made. It exists so bad limit/offset values fail fast in the renderer instead of surfacing as confusing core-side errors.","triggerScenarios":"Calling agentTeamApi.list({ limit: 0 }), list({ limit: -5 }), list({ offset: 2.5 }), or list({ limit: NaN }) — i.e. any call where limit/offset is defined but fails Number.isInteger(value) || value <= 0. Note assertPositiveInt is also reused by listMessages (line 216) with the label 'limit'.","commonSituations":"UI pagination math that computes limit as pageEnd - pageStart and yields 0 on an empty page; passing a float from a slider or a parsed query-string param (e.g. Number('2.5')); defaults accidentally set to 0; NaN propagation from Number(undefined).","solutions":["Fix the caller to pass undefined instead of 0 when no cap is intended — undefined skips the check entirely","Clamp computed pagination values before calling: limit = Math.max(1, Math.floor(limit))","If 0 legitimately means 'no limit' in your UI, translate it: params.limit || undefined","Trace where the non-integer originates (query param parsing, division) and round it"],"exampleFix":"// before\nconst teams = await agentTeamApi.list({ limit: pageEnd - pageStart }); // 0 on empty page\n\n// after\nconst teams = await agentTeamApi.list({ limit: Math.max(1, pageEnd - pageStart) });\n// or omit entirely when there is nothing to page:\nconst teams = await agentTeamApi.list(pageEnd - pageStart > 0 ? { limit: pageEnd - pageStart } : {});","handlingStrategy":"validation","validationCode":"function safePage(v: number | undefined): number | undefined {\n  if (v === undefined) return undefined;\n  const n = Math.floor(v);\n  return Number.isFinite(n) && n > 0 ? n : undefined;\n}\n// before the call:\nconst params = { status, limit: safePage(limit), offset: safePage(offset) };\nif (limit !== undefined && params.limit === undefined) throw new Error('bad limit');","typeGuard":"const isPositiveInt = (v: unknown): v is number =>\n  typeof v === 'number' && Number.isInteger(v) && v > 0;","tryCatchPattern":"try { await agentTeamApi.list({ limit }); }\ncatch (e) { if (String(e.message).includes('positive integer')) { limit = undefined; retryWithoutCap(); } else throw e; }","preventionTips":["Treat 0/undefined as the same 'no cap' at your call boundary and normalize once","Never pass raw parsed query-string numbers straight into pagination params","Centralize page-size computation in one helper so the clamp lives in one place"],"tags":["validation","pagination","agent-teams","typescript"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}