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

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions