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
- Guard at the call site: skip (or no-op) when the trimmed id is empty — there is nothing to cancel
- Only render/enable the cancel control when the row actually has a non-empty taskId
- Fix the upstream shape: if the spawn result is missing taskId, that is the real bug — inspect what produced the row data
- 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
- Only render cancel controls for rows with a non-empty taskId
- Type row models so taskId is present where cancel is offered
- Treat this throw as a lint-level bug: it should never fire in production code
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
- worktreeApi.status: path is required
- worktreeApi.diff: path is required
- worktreeApi.remove: path is required
- Finish choosing how OpenHuman runs (tap Continue on the setu
- OpenHuman could not reach its remote (cloud) runtime. Check
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/7982ea1b4f5ac2ad.
Report an issue: GitHub.