tinyhumansai/openhuman · error

agentTeamApi.startMember: teamId and memberId are required

Error message

agentTeamApi.startMember: teamId and memberId are required

What it means

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.

Source

Thrown at app/src/services/api/agentTeamApi.ts:326

      payload: readMessagePayload(event.payload),
      timestamp: event.timestamp,
    };
  },

  /**
   * Start a live run for a member: the core claims a task (the explicit `taskId`,
   * else the member's next claimable one) and spawns a worker that runs it to
   * completion. Returns a {@link StartMemberOutcome} — `started` (worker
   * dispatched) or a reason no work began.
   */
  startMember: async (params: {
    teamId: string;
    memberId: string;
    taskId?: string;
  }): Promise<StartMemberOutcome> => {
    const { teamId, memberId, taskId } = params;
    if (!teamId || !memberId) {
      throw new Error('agentTeamApi.startMember: teamId and memberId are required');
    }
    log('startMember teamId=%s memberId=%s taskId=%o', teamId, memberId, taskId);
    const response = await callCoreRpc<{ result: StartMemberOutcome }>({
      method: 'openhuman.agent_team_start_member',
      params: { teamId, memberId, ...(taskId ? { taskId } : {}) },
    });
    log('startMember kind=%s', response.result.kind);
    return response.result;
  },
};

View on GitHub (pinned to a221052e0d)

Solutions

  1. Gate the start action on both ids being non-empty
  2. Bind the handler to the full member row so both ids come from one object
  3. Show a disabled state with a tooltip while data loads instead of relying on the throw

Example fix

// before
<button onClick={() => agentTeamApi.startMember({ teamId, memberId: row.id ?? '' })}>Start</button>

// after
<button disabled={!teamId || !row.id} onClick={() => agentTeamApi.startMember({ teamId, memberId: row.id })}>Start</button>
Defensive patterns

Strategy: validation

Validate before calling

const canStart = Boolean(teamId && member?.id);
if (canStart) await agentTeamApi.startMember({ teamId, memberId: member.id });

Type guard

const isNonEmptyString = (v: unknown): v is string => typeof v === 'string' && v.length > 0;

Try / catch

try { const out = await agentTeamApi.startMember({ teamId, memberId }); }
catch (e) { if (String(e.message).includes('required')) disableStartButton(); else throw e; }

Prevention

When it happens

Trigger: Calling startMember({ memberId }) with no teamId, or startMember({ teamId, memberId: '' }) — e.g. a 'Run' button on a member card before the team context resolves.

Common situations: 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.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/14578ae26753aaf1. Report an issue: GitHub.