Skip to content

Fix #896: use asyncio.run() so idb launches on Python 3.10+#924

Open
DrunkOnJava wants to merge 1 commit into
facebook:mainfrom
DrunkOnJava:fix/python-3.14-asyncio
Open

Fix #896: use asyncio.run() so idb launches on Python 3.10+#924
DrunkOnJava wants to merge 1 commit into
facebook:mainfrom
DrunkOnJava:fix/python-3.14-asyncio

Conversation

@DrunkOnJava
Copy link
Copy Markdown

Summary

idb/cli/main.py::main() calls asyncio.get_event_loop() at top-level, which on Python ≥ 3.10 raises:

RuntimeError: There is no current event loop in thread 'MainThread'

…when no event loop has been explicitly set on the main thread. Python 3.10 deprecated implicit-loop creation; 3.12+ enforces it as a hard error. By 3.14 (now the default for brew install python on macOS) idb will not launch at all — every invocation crashes during startup. This blocks idb, idb udid, idb list-targets, etc. for any user on a modern Python.

The fix

Replace the manual asyncio.get_event_loop() + loop.run_until_complete() + loop.close() pattern with asyncio.run(...). This is the canonical CLI entrypoint introduced in Python 3.7. It:

  • Creates a fresh event loop (no implicit-loop semantics)
  • Runs the coroutine to completion
  • Cancels pending tasks and closes the loop in a single call
  • Is the documented Python idiom for synchronous-to-async transitions in CLI tools

Behaviour is identical to the previous pattern on Python 3.7-3.9 (where get_event_loop() auto-created a loop when none existed), and now also works on 3.10-3.14+.

Context

  • Reported as There is no current event loop in thread 'MainThread'. #896 (2025-11-26, marked completed 2026-02-04 with a user-workaround comment but never patched upstream).
  • @zetlen confirmed it was still reproducing as of 2026-04-10.
  • I re-confirmed it on fb-idb 1.1.7 / Python 3.14.2 (current brew defaults) today.
  • Local workaround in our deployment: a patch-idb.sh script that rewrites the function in-place after every brew upgrade. This PR upstreams the fix so that's no longer necessary for anyone.

Test plan

  • Install on a Python 3.14 environment (pipx install --python python3.14 fb-idb from this branch's wheel)
  • Run idb list-targets — should print target list (not RuntimeError)
  • Run idb udid — should print UDID (not RuntimeError)
  • Run any subcommand that exits non-zero — verify the process actually exits non-zero (asyncio.run propagates the return value through sys.exit(main()) in main_2())
  • Re-run unchanged on Python 3.9 (the documented minimum) — should keep working

Notes

This is a one-line semantic change wrapped in a comment block explaining the why. The previous form's try/finally was redundant given that asyncio.run() already handles loop cleanup; removing it is intentional, not a bug.

Python 3.10 deprecated implicit-loop creation in asyncio.get_event_loop();
3.12+ raises RuntimeError("There is no current event loop in thread")
unless an event loop is explicitly set on the current thread. By
Python 3.14 (the default brew formula now) calling 'idb' or 'idb
udid' immediately crashes:

  RuntimeError: There is no current event loop in thread 'MainThread'

asyncio.run() is the canonical CLI entrypoint introduced in Python 3.7.
It creates a fresh event loop, runs the coroutine, and closes the loop
(including cancelling pending tasks) in one call — exactly what the
existing get_event_loop()+close() block was doing manually, but without
the deprecated implicit-loop creation.

Closes facebook#896 (reported 2025-11-26, marked completed in 2026-02 with a
user workaround comment but never patched upstream; still reproducing
on fb-idb 1.1.7 / Python 3.14.2 as of 2026-04-10).
Copilot AI review requested due to automatic review settings May 18, 2026 13:07
@meta-cla
Copy link
Copy Markdown

meta-cla Bot commented May 18, 2026

Hi @DrunkOnJava!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Replaces the deprecated asyncio.get_event_loop() + run_until_complete() pattern in the CLI entrypoint with asyncio.run(), fixing a startup RuntimeError on Python 3.10+ (issue #896).

Changes:

  • Swap manual loop management for asyncio.run(gen_main(cmd_input)) in idb/cli/main.py::main().
  • Add an explanatory comment block documenting the rationale and Python version context.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants