google-gemini/gemini-cli · error

A2AClient cancelTask Error [${agentName}]: ${error.message}

Error message

A2AClient cancelTask Error [${agentName}]: ${error.message}

What it means

cancelTask wraps any Error thrown by the underlying SDK client.cancelTask({ id: taskId }) with an 'A2AClient cancelTask Error [<agentName>]' prefix and preserves the original via { cause }. The inner message is the real signal: it is typically a JSON-RPC error, a network failure, or a task-state rejection from the remote agent. The wrapping makes the failing operation and agent traceable in logs.

Source

Thrown at packages/core/src/agents/a2a-client-manager.ts:286

      throw new Error(`${prefix}: Unexpected error: ${String(error)}`);
    }
  }

  /**
   * Cancels a task on an agent.
   * @param agentName The name of the agent.
   * @param taskId The ID of the task to cancel.
   * @returns The cancellation response.
   */
  async cancelTask(agentName: string, taskId: string): Promise<Task> {
    const client = this.clients.get(agentName);
    if (!client) throw new Error(`Agent '${agentName}' not found.`);
    try {
      return await client.cancelTask({ id: taskId });
    } catch (error: unknown) {
      const prefix = `A2AClient cancelTask Error [${agentName}]`;
      if (error instanceof Error) {
        throw new Error(`${prefix}: ${error.message}`, { cause: error });
      }
      throw new Error(`${prefix}: Unexpected error: ${String(error)}`);
    }
  }
}

View on GitHub (pinned to 5024443c72)

Solutions

  1. Inspect error.cause for the real upstream message and HTTP/JSON-RPC code before deciding to retry.
  2. If the task is already terminal, treat the cancel as a no-op success rather than an error.
  3. Verify the taskId came from a prior sendMessage/getTask response for the same agent and contextId.
  4. For transient network errors, retry cancelTask with backoff (the SDK auth-retry is separate and only covers 401/403).

Example fix

// before
const task = await manager.cancelTask(name, taskId);

// after
try {
  const task = await manager.cancelTask(name, taskId);
} catch (e) {
  const msg = e instanceof Error ? e.cause instanceof Error ? e.cause.message : e.message : String(e);
  if (/already.*(cancel|terminal|completed|failed)/i.test(msg)) {
    // already done - ignore
  } else {
    throw e;
  }
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await manager.cancelTask(agentName, taskId);
} catch (e) {
  const cause = e instanceof Error ? e.cause : undefined;
  const inner = cause instanceof Error ? cause.message : String(e);
  // Treat already-terminal as success
  if (/cancel|terminal|completed|failed/i.test(inner)) {
    return await manager.getTask(agentName, taskId);
  }
  throw e;
}

Prevention

When it happens

Trigger: The task id does not exist on the remote agent; the task is already in a terminal state (completed/failed/canceled) and cannot be canceled; the remote A2A server returned a JSON-RPC error; a transient network error or timeout (up to the 30-minute A2A_TIMEOUT) interrupted the request; the auth token expired and retry was exhausted.

Common situations: Stale taskId persisted from a previous session and reused after the remote purged it; cancelling a task that already finished; remote agent restarted and lost in-memory task state; proxy or TLS misconfiguration causing the SDK fetch to reject.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/a5133c9757320957. Report an issue: GitHub.