Skip to content

[Bug] Instant "Task aborted" error when executing tasks via MCP due to React re-render & PageAgent disposal #570

Description

@waynez

What happened? / 问题描述

Problem Description

When executing browser tasks through the page-agent MCP server, the task immediately fails with a "Task failed. Task aborted" error.

In the browser console of the Page Agent Hub, we can see the log:

Disposing PageAgent...
background.js:29 [RemotePageController.background] Error: Could not establish connection. Receiving end does not exist.

Root Cause Analysis

The root cause lies in a race condition between the imperative execution cycle of the Agent and the declarative lifecycle of React hooks inside the Chrome Extension.

  1. MCP passes config on every execution: Inside @page-agent/mcp/src/index.js, every time execute_task is triggered, the MCP client sends the LLM environment configuration (e.g. LLM_BASE_URL, LLM_API_KEY) to the Hub via WebSocket:
const config = Object.keys(llmConfig).length > 0 ? llmConfig : undefined;
const result = await hub.executeTask(task, config);
  1. Hub WS handler immediately triggers re-configuration: In useHubWs.ts (or hub-DUJlnjFc.js), when the WebSocket receives the execute event, it calls configure first, followed by execute(task):
onExecute: async (task, incomingConfig) => {
    const { execute, configure, config } = latestRef.current;
    if (incomingConfig) await configure({ ...config, ...incomingConfig }); // 1. Updates state asynchronously
    const result = await execute(task);                                     // 2. Starts execution
  1. React State update unmounts the current Agent: In useAgent.ts, the hook instantiates MultiPageAgent inside a useEffect with [config] as a dependency:
useEffect(() => {
    if (!config) return;
    const agent = new MultiPageAgent({ ...config });
    agentRef.current = agent;
    // ...
    return () => {
        agent.dispose(); // ⚠️ Run cleanup whenever config reference changes!
    };
}, [config]);
  1. dispose() aborts the active task: Because configure triggers setConfig, React schedules a re-render. Since config's reference has changed, the cleanup function of the previous useEffect is immediately executed. This calls agent.dispose(), which executes this.#abortController.abort(), instantly killing the task that was just started.

How to reproduce / 如何复现

  1. Install chrome extension and configure MCP in AI agent, e.g., Cursor
  2. Send a command in AI agent invoking the MCP

Version

No response

Browser

Chrome: 149.0.7827.116
Extension: 1.10.0

Before submitting

  • I will be polite and respectful. / 我会保持礼貌与尊重。
  • I have read the Code of Conduct. / 我已阅读行为准则。
  • I have searched existing issues and this is not a duplicate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions