alibaba/page-agent · warning · Error

Agent is already running a task.

Error message

Agent is already running a task.

What it means

HubBridge.executeTask refuses to start a second concurrent task: if #pendingTask is set (a previous executeTask promise is still unresolved), it throws this error. The bridge supports one in-flight task at a time.

Source

Thrown at packages/mcp/src/hub-bridge.js:86

		})
	}

	get connected() {
		return this.#hub?.readyState === 1
	}

	get busy() {
		return this.#pendingTask !== null
	}

	/**
	 * @param {string} task
	 * @param {Record<string, unknown>} [config]
	 * @returns {Promise<{success: boolean, data: string}>}
	 */
	async executeTask(task, config) {
		if (!this.connected) throw new Error('Hub is not connected. Is the extension running?')
		if (this.#pendingTask) throw new Error('Agent is already running a task.')

		return new Promise((resolve, reject) => {
			this.#pendingTask = { resolve, reject }
			this.#hub.send(JSON.stringify({ type: 'execute', task, config }))
		})
	}

	stopTask() {
		if (this.connected) {
			this.#hub.send(JSON.stringify({ type: 'stop' }))
		}
	}

	// TODO: Add version checking

	/** @param {import('ws').WebSocket} ws */
	#onConnection(ws) {
		if (this.#hub && this.#hub.readyState === 1) {

View on GitHub (pinned to d02db1ee7c)

Solutions

  1. Serialize task execution: await the previous executeTask promise (or a queue) before issuing the next
  2. If a task was abandoned by a client timeout, disconnect/reconnect the bridge to reset state, or add a cancel mechanism if available
  3. On the client side, set timeouts generous enough for page automation tasks to finish
  4. Guard with a check of the bridge's busy state before dispatching

Example fix

// before
const [a, b] = await Promise.all([
  bridge.executeTask(task1),
  bridge.executeTask(task2), // throws 'already running'
])

// after
const a = await bridge.executeTask(task1)
const b = await bridge.executeTask(task2) // run sequentially
Defensive patterns

Strategy: validation

Validate before calling

let chain: Promise<unknown> = Promise.resolve()
function runTask(task: string) {
  const next = chain.then(() => bridge.executeTask(task))
  chain = next.catch(() => {}) // keep queue alive on failures
  return next
}

Type guard

null

Try / catch

try {
  await bridge.executeTask(task)
} catch (e) {
  if (e instanceof Error && e.message.includes('already running')) {
    await delay(1000) // or await the in-flight promise
    return bridge.executeTask(task)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling executeTask() again while a previous task is still awaiting its result — e.g. concurrent MCP tool invocations, a client timeout that abandons the promise without the bridge clearing it, or firing two requests in parallel.

Common situations: MCP client sending parallel tool calls; aggressive client-side timeouts that give up on a slow task but leave it pending in the bridge; missing await causing accidental double invocation; long-running automation tasks overlapping.

Related errors


AI-assisted analysis of alibaba/page-agent@d02db1ee7c (2026-08-28). Data as JSON: /api/errors/1487947bb2bce2f5. Report an issue: GitHub.