#5429·nanobot

AgentLoop does not retrieve exceptions from background tasks

Author: yu-xin-cCreated Aug 18, 2026Updated Sep 10, 2026

Description

AgentLoop.schedule_background() tracks a task until completion, then removes it with set.discard:

task = asyncio.create_task(coro)
self._background_tasks.add(task)
task.add_done_callback(self._background_tasks.discard)

The completion callback never calls task.result() or task.exception(). When a background coroutine fails, the task is removed from the owning set without its exception being retrieved, so asyncio can only emit the generic Task exception was never retrieved warning.

Reproduction

Tested on current main at d47efcc352abaab78b218a9e7c711bfb7a249925.

I installed an event-loop exception handler, scheduled a coroutine that raises RuntimeError, and let the task finish:

tracked_tasks_after_failure: 0
loop_errors: ["Task exception was never retrieved"]
exceptions: ["RuntimeError"]

The task registry is clean, but the failure is detached from the AgentLoop lifecycle and only reaches the generic asyncio handler.

Impact

This helper owns work such as post-turn consolidation, asynchronous session archival, WebUI title generation, and background command execution. Individual jobs may handle expected failures, but an unexpected exception currently has no common terminal observer and can be easy to miss in a long-running service.

Expected behavior

The task completion callback should:

  • remove the completed task from _background_tasks;
  • ignore normal cancellation;
  • retrieve any exception;
  • log unexpected failures with traceback and useful task context;
  • preserve the existing shutdown behavior that drains pending background tasks.

Suggested regression coverage

  • Successful tasks are removed without an error log.
  • Failed tasks are removed, retrieved, and logged without an unhandled-loop warning.
  • Cancelled tasks are removed without being reported as failures.