tinyhumansai/openhuman · warning

subagentApi.cancel: taskId is required

Error message

subagentApi.cancel: taskId is required

What it means

A client-side precondition guard in subagentApi.cancel(): it trims the taskId argument and throws before any RPC if the result is empty. The underlying 'openhuman.subagent_cancel' RPC needs a spawn task id; the guard turns an obvious programming mistake into an immediate, descriptive error instead of a pointless core round-trip.

Source

Thrown at app/src/services/api/subagentApi.ts:31

const log = debug('subagentApi');

/** Result of a cancel request. Mirrors the Rust handler payload. */
interface SubagentCancelResult {
  /** True if a running sub-agent was aborted; false if it was already done/unknown. */
  cancelled: boolean;
  taskId: string;
}

export const subagentApi = {
  /**
   * Cancel a still-running detached background sub-agent by its spawn task id.
   * Resolves with `cancelled: false` (not an error) when the sub-agent already
   * finished or the id is unknown.
   */
  cancel: async (taskId: string, reason?: string): Promise<SubagentCancelResult> => {
    const id = taskId.trim();
    if (!id) throw new Error('subagentApi.cancel: taskId is required');
    const params: Record<string, unknown> = { taskId: id };
    const trimmedReason = reason?.trim();
    if (trimmedReason) params.reason = trimmedReason;
    log('cancel taskId=%s', id);
    const result = await callCoreRpc<SubagentCancelResult>({
      method: 'openhuman.subagent_cancel',
      params,
    });
    log('cancel received cancelled=%s', result.cancelled);
    return result;
  },
};

View on GitHub (pinned to a221052e0d)

Solutions

  1. Guard at the call site: skip (or no-op) when the trimmed id is empty — there is nothing to cancel
  2. Only render/enable the cancel control when the row actually has a non-empty taskId
  3. Fix the upstream shape: if the spawn result is missing taskId, that is the real bug — inspect what produced the row data
  4. In tests, pass a real id like 'task-123' instead of ''

Example fix

// before
onCancel={() => subagentApi.cancel(row.taskId)}

// after
onCancel={() => {
  const id = row.taskId?.trim();
  if (!id) return; // nothing to cancel
  subagentApi.cancel(id);
}}
Defensive patterns

Strategy: validation

Validate before calling

const id = taskId?.trim();
if (!id) {
  // Nothing to cancel — treat as the API's own 'already finished' semantics
  return;
}
await subagentApi.cancel(id, reason);

Prevention

When it happens

Trigger: Calling subagentApi.cancel(''), cancel(' '), or cancel(someUndefined) (undefined.trim() would throw earlier — the usual case is an empty string). Typically a UI cancel button wired to a row whose taskId never got populated because the spawn response lacked it.

Common situations: A background-work list renders rows from a payload where taskId is optional, and the cancel handler assumes it is always present; a 'cancel all' loop passes a default '' placeholder; refactoring changed the row model and taskId moved/nested.

Related errors


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