parallel tool: refresh_parallel_jobs awaits task.result() unbounded, bypassing the wait timeout (permanent loop stall on hung job) #101
Labels
No labels
accessibility
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
nsaspy/a0-symbolics#101
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The parallel tool's wait loop checks its deadline only after refreshing job results, and the refresh awaits
task.result()with no timeout. A background job that never completes (e.g. a wrapped tool blocked on a synchronous call) parksrefresh_parallel_jobsinside a blockingconcurrent.futures.Future.result()on a cross-loop future forever — the tool call never returns, thetimeoutguard is never reached, and the agent loop stalls permanently.Location
helpers/parallel_tools.py:247-262— poll loop:await await_parallel_jobs(agent)runs before the deadline check at:258.helpers/parallel_tools.py:308-311— for a ready task:await task.result()with no timeout.helpers/defer.py:183-199—DeferredTask.result()parks a blockingfuture.result(timeout=None)on a default-executor thread (sync-fetch over cross-loop future).936713bc, 2026-08-24); reviewed at main5116eb4d.Repro
wait: false,tool_calls: [{tool_name: "code_execution_tool", tool_args: {runtime: "python", code: "import time; time.sleep(9999)"}}](or any tool that blocks synchronously insideexecute).action: "await",job_ids: [...],timeout: 10.timeoutis never honored: the refresh blocks indefinitely insidetask.result()before the deadline check ever runs. Only a UI-initiated job cancel can break the stall.Impact
task.result()also occupies a thread of the shared defaultThreadPoolExecutor; concurrent chats doing this starve unrelatedrun_in_executorusers framework-wide.Suggested fix
DeferredTask.result(timeout=...)(orasyncio.wait_foraround it) so the deadline check atparallel_tools.py:258is authoritative.task.result()waits to avoid shared-executor starvation.Secondary findings (same subsystem, can be split out)
wait: falsejobs leak: no teardown hook removesPARALLEL_JOBS_KEYentries or cancels runningDeferredTasks at context end (parallel_tools.py:215, 273-297); orphaned coroutines keep the background loop busy and results are lost.helpers/defer.py:21-26,parallel_tools.py:222), so a blocking wrapped tool head-of-line blocks sibling jobs plus memory/history background extensions.