# NousResearch/hermes-agent · Cron asyncio coroutine cleanup on RuntimeError

## Open now

- Nothing open on GitHub.

These items address a bug in the cron component where a coroutine is left unawaited or abandoned when asyncio.run() raises a RuntimeError. The fixes focus on properly closing that coroutine to avoid resource leaks.

## TL;DR

- 5 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #2317. Verify it addresses your case.
- **Recommended:** read PR #2317 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-2317`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.40`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #2138: fix(cron): unawaited coroutine when asyncio.run() hits RuntimeError

## Prior & competing PRs

- **#2142: fix(cron): close unawaited coroutine when asyncio.run() hits RuntimeError**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#2248: fix(cron): close unawaited coroutine when asyncio.run() hits RuntimeError**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#2317: fix(cron): close abandoned coroutine when asyncio.run() raises RuntimeError**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `cron/scheduler.py`
  - fix hunks:
  - `cron/scheduler.py`

    ```diff
    @@ -170,11 +170,15 @@ def _deliver_result(job: dict, content: str) -> None:
         )
     
         # Run the async send in a fresh event loop (safe from any thread)
    +    coro = _send_to_platform(platform, pconfig, chat_id, wrapped, thread_id=thread_id)
         try:
    -        result = asyncio.run(_send_to_platform(platform, pconfig, chat_id, wrapped, thread_id=thread_id))
    +        result = asyncio.run(coro)
         except RuntimeError:
    -        # asyncio.run() fails if there's already a running loop in this thread;
    -        # spin up a new thread to avoid that.
    +        # asyncio.run() checks for a running loop before awaiting the coroutine;
    +        # when it raises, the original coro was never started — close it to
    +        # prevent "coroutine was never awaited" RuntimeWarning, then retry in a
    +        # fresh thread that has no running loop.
    +        coro.close()
             import concurrent.futures
             with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
                 future = pool.submit(asyncio.run, _send_to_platform(platform, pconfig, chat_id, wrapped, thread_id=thread_id))
    ```
- **#2290: fix(cron): close abandoned coroutine when asyncio.run() raises RuntimeError**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#2317`

## Fix status

- Merged to main in PR #2317 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-03-21`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `cron`
