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
- Serialize task execution: await the previous executeTask promise (or a queue) before issuing the next
- If a task was abandoned by a client timeout, disconnect/reconnect the bridge to reset state, or add a cancel mechanism if available
- On the client side, set timeouts generous enough for page automation tasks to finish
- 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
- Serialize executeTask calls through a queue; never fire them concurrently
- Await each task fully before starting the next
- Set client timeouts longer than the worst-case automation task
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.