描述
调用取消 API(POST /tasks/{id}/cancel)后,DB 状态被写为 cancelled,但正在执行中的 SubAgent runtime 完成后会将状态覆盖为 done,导致取消无效。
复现步骤
- 派遣一个执行时间较长的 SubAgent 任务(例如需要网络请求的 research 任务)
- 在任务运行中调用
POST /api/v1/tasks/{task_id}/cancel
- API 返回
{"status": "cancelled"}
- 等待任务执行完毕后,重新查询任务状态
- 发现任务状态变为
done 而非 cancelled
期望行为
取消后任务状态应保持 cancelled,不应被 runtime 的最终写入覆盖。
根因分析
agentpal/runtimes/ 中的执行路径在任务完成后直接写入 task.status = DONE,没有先检查任务是否已被外部取消(即检查 DB 中当前状态是否为 CANCELLED)。
建议修复
在 runtime 写入最终状态前,先 await db.refresh(task) 重新加载任务状态,若已为 CANCELLED 则跳过状态更新:
await db.refresh(task)
if task.status != TaskStatus.CANCELLED:
task.status = TaskStatus.DONE
task.result = result.output
await db.commit()
影响
- 前端任务页面"已取消"过滤器无法找到被取消的任务
- 用户无法可靠地终止不需要的任务
环境
描述
调用取消 API(
POST /tasks/{id}/cancel)后,DB 状态被写为cancelled,但正在执行中的 SubAgent runtime 完成后会将状态覆盖为done,导致取消无效。复现步骤
POST /api/v1/tasks/{task_id}/cancel{"status": "cancelled"}done而非cancelled期望行为
取消后任务状态应保持
cancelled,不应被 runtime 的最终写入覆盖。根因分析
agentpal/runtimes/中的执行路径在任务完成后直接写入task.status = DONE,没有先检查任务是否已被外部取消(即检查 DB 中当前状态是否为CANCELLED)。建议修复
在 runtime 写入最终状态前,先
await db.refresh(task)重新加载任务状态,若已为CANCELLED则跳过状态更新:影响
环境