{"record":{"id":"14578ae26753aaf1","repo":"tinyhumansai/openhuman","slug":"agentteamapi-startmember-teamid-and-memberid-are","errorCode":null,"errorMessage":"agentTeamApi.startMember: teamId and memberId are required","messagePattern":"agentTeamApi\\.startMember: teamId and memberId are required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/api/agentTeamApi.ts","lineNumber":326,"sourceCode":"      payload: readMessagePayload(event.payload),\n      timestamp: event.timestamp,\n    };\n  },\n\n  /**\n   * Start a live run for a member: the core claims a task (the explicit `taskId`,\n   * else the member's next claimable one) and spawns a worker that runs it to\n   * completion. Returns a {@link StartMemberOutcome} — `started` (worker\n   * dispatched) or a reason no work began.\n   */\n  startMember: async (params: {\n    teamId: string;\n    memberId: string;\n    taskId?: string;\n  }): Promise<StartMemberOutcome> => {\n    const { teamId, memberId, taskId } = params;\n    if (!teamId || !memberId) {\n      throw new Error('agentTeamApi.startMember: teamId and memberId are required');\n    }\n    log('startMember teamId=%s memberId=%s taskId=%o', teamId, memberId, taskId);\n    const response = await callCoreRpc<{ result: StartMemberOutcome }>({\n      method: 'openhuman.agent_team_start_member',\n      params: { teamId, memberId, ...(taskId ? { taskId } : {}) },\n    });\n    log('startMember kind=%s', response.result.kind);\n    return response.result;\n  },\n};\n","sourceCodeStart":308,"sourceCodeEnd":337,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/agentTeamApi.ts#L308-L337","documentation":"Guard inside agentTeamApi.startMember: both teamId and memberId must be non-empty before the openhuman.agent_team_start_member RPC claims a task and spawns the member's worker. taskId is optional (the core then claims the next claimable task), but the two identity fields are mandatory.","triggerScenarios":"Calling startMember({ memberId }) with no teamId, or startMember({ teamId, memberId: '' }) — e.g. a 'Run' button on a member card before the team context resolves.","commonSituations":"Start action enabled by default on skeleton/loading rows; member id read from an optional field of a differently-shaped object; team id kept in a parent that has not mounted its value yet.","solutions":["Gate the start action on both ids being non-empty","Bind the handler to the full member row so both ids come from one object","Show a disabled state with a tooltip while data loads instead of relying on the throw"],"exampleFix":"// before\n<button onClick={() => agentTeamApi.startMember({ teamId, memberId: row.id ?? '' })}>Start</button>\n\n// after\n<button disabled={!teamId || !row.id} onClick={() => agentTeamApi.startMember({ teamId, memberId: row.id })}>Start</button>","handlingStrategy":"validation","validationCode":"const canStart = Boolean(teamId && member?.id);\nif (canStart) await agentTeamApi.startMember({ teamId, memberId: member.id });","typeGuard":"const isNonEmptyString = (v: unknown): v is string => typeof v === 'string' && v.length > 0;","tryCatchPattern":"try { const out = await agentTeamApi.startMember({ teamId, memberId }); }\ncatch (e) { if (String(e.message).includes('required')) disableStartButton(); else throw e; }","preventionTips":["Gate start controls on both ids from the same row object","Keep optional taskId optional — only the two identity fields are hard requirements","Show loading placeholders as disabled, not as actionable rows"],"tags":["validation","agent-teams","required-field"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}