{"record":{"id":"67b48ce241da7ed3","repo":"tinyhumansai/openhuman","slug":"agentteamapi-shutdownmember-teamid-and-memberid-a","errorCode":null,"errorMessage":"agentTeamApi.shutdownMember: teamId and memberId are required","messagePattern":"agentTeamApi\\.shutdownMember: teamId and memberId are required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/api/agentTeamApi.ts","lineNumber":266,"sourceCode":"    if (!teamId || !taskId || !memberId) {\n      throw new Error('agentTeamApi.completeTask: teamId, taskId and memberId are required');\n    }\n    log('completeTask teamId=%s taskId=%s requireEvidence=%o', teamId, taskId, requireEvidence);\n    const response = await callCoreRpc<{ result: CompletionOutcome }>({\n      method: 'openhuman.agent_team_complete_task',\n      params: { teamId, taskId, memberId, evidence, requireEvidence },\n    });\n    log('completeTask kind=%s', response.result.kind);\n    return response.result;\n  },\n\n  /**\n   * Stop a member and release any task it is actively working on back to `todo`.\n   * Returns the stopped member plus the ids that were released.\n   */\n  shutdownMember: async (teamId: string, memberId: string): Promise<MemberShutdown> => {\n    if (!teamId || !memberId) {\n      throw new Error('agentTeamApi.shutdownMember: teamId and memberId are required');\n    }\n    log('shutdownMember teamId=%s memberId=%s', teamId, memberId);\n    const response = await callCoreRpc<{ result: MemberShutdown }>({\n      method: 'openhuman.agent_team_shutdown_member',\n      params: { teamId, memberId },\n    });\n    log('shutdownMember released=%d', response.result.releasedTaskIds.length);\n    return response.result;\n  },\n\n  /**\n   * Send a message to a named teammate (or broadcast when `toMemberId` is\n   * omitted). `fromMemberId` is omitted for a lead/user-originated message — the\n   * core stores it with `from = \"lead\"`. Returns the appended message event.\n   */\n  messageMember: async (params: {\n    teamId: string;\n    toMemberId?: string;","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/agentTeamApi.ts#L248-L284","documentation":"Guard inside agentTeamApi.shutdownMember: both teamId and memberId must be non-empty before the openhuman.agent_team_shutdown_member RPC stops the member and releases its tasks. Shutting down 'nothing' is meaningless, so the client rejects it up front.","triggerScenarios":"Calling shutdownMember('', memberId) or shutdownMember(teamId, '') — e.g. a stop button wired to a member card whose id prop is missing, or the team context not yet loaded.","commonSituations":"Row actions rendered from sparse data; the member already removed from the list by the time the handler runs (stale closure holding empty id); optional-chained state defaulting to undefined.","solutions":["Derive both ids from the same source-of-truth object (the member row) right at call time","Disable the stop control until the member row (with teamId) is loaded","If the member may already be gone, treat a missing id as a no-op in the UI instead of calling"],"exampleFix":"// before\nonClick={() => agentTeamApi.shutdownMember(teamId, member?.id ?? '')}\n\n// after\nonClick={() => {\n  if (!teamId || !member?.id) return;\n  agentTeamApi.shutdownMember(teamId, member.id).catch(notify);\n}}","handlingStrategy":"validation","validationCode":"const canShutdown = typeof teamId === 'string' && teamId !== '' && typeof memberId === 'string' && memberId !== '';\nif (canShutdown) await agentTeamApi.shutdownMember(teamId, memberId);","typeGuard":"const hasBothIds = (t: unknown, m: unknown): t is string & { memberId: string } => false; // prefer explicit checks\nconst ok = (v: unknown): v is string => typeof v === 'string' && v.length > 0;","tryCatchPattern":"try { await agentTeamApi.shutdownMember(teamId, memberId); }\ncatch (e) { if (String(e.message).includes('required')) refreshMembers(); else throw e; }","preventionTips":["Bind row actions to the full member row captured at render, not stale closures","Treat a missing member row as 'already gone' and refresh the list","Disable destructive controls during skeleton/loading states"],"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"}