Skip to content

fix(cli,session): remove nest_asyncio, make chat-profile config async-safe#2953

Open
pidefrem wants to merge 3 commits into
Chainlit:mainfrom
pidefrem:fix/remove-nest-asyncio-python-3-14
Open

fix(cli,session): remove nest_asyncio, make chat-profile config async-safe#2953
pidefrem wants to merge 3 commits into
Chainlit:mainfrom
pidefrem:fix/remove-nest-asyncio-python-3-14

Conversation

@pidefrem

@pidefrem pidefrem commented Jun 8, 2026

Copy link
Copy Markdown

Summary

Problem 1 — nest_asyncio breaks Python 3.14

nest_asyncio.apply() is called at import time in chainlit/cli/__init__.py.
nest_asyncio <= 1.6.0 internally calls asyncio.ensure_future(future, loop=self),
where the loop= keyword was removed in Python 3.14 (bpo-39529).
This corrupts asyncio task registration, causing asyncio.current_task() to return
None inside running coroutines.

The symptom is a white page on every Chainlit app running on Python 3.14:
starlette's FileResponse calls anyio.to_thread.run_sync(), which calls
sniffio.current_async_library(), which calls asyncio.current_task() -> None
-> anyio.NoEventLoopError -> HTTP 500 on every static asset.

Problem 2 — get_config() relied on nest_asyncio for re-entrant event loop

WebsocketSession.get_config() called asyncio.get_event_loop().run_until_complete()
from within the already-running event loop (the SocketIO connect handler).
This only worked because nest_asyncio patched run_until_complete to be re-entrant.
Removing nest_asyncio without fixing this would silently break chat-profile
config overrides on all Python versions (3.10-3.13), not just 3.14.

Fix

fix(cli): remove nest_asyncio entirely.
asyncio.run(start()) is a top-level entry point; it does not need re-entrant
loop support. Also removes the nest-asyncio dependency and its mypy override.
Adds a regression test to prevent silent reintroduction.

fix(session): make chat-profile config resolution async-safe.

  • get_config() now returns the cached config immediately (no run_until_complete)
  • New async resolve_config() method properly awaits set_chat_profiles()
  • connect() handler calls await session.resolve_config() after construction
  • 9 new tests covering overrides, idempotency, error handling, unknown profiles,
    and a regression test verifying run_until_complete is never called
  • Adds hatchling to the mypy optional deps (it ships py.typed;
    fixes a pre-existing import-not-found mypy error in build.py)

test(e2e): harden Chainlit process cleanup and Cypress CI.

  • Kills the entire Chainlit process group (uv -> chainlit -> uvicorn)
    between specs and polls until the port is free, resolving an EADDRINUSE race
  • Excludes .pytest_cache from Prettier checks
  • Ensures the Cypress binary is present before tests run (a restored-but-incomplete
    cache on windows-latest left the npm package installed while the binary was
    missing) and sets fail-fast: false so one shard's flake no longer cancels the
    rest of the matrix

Testing

All 798 tests pass on Python 3.10, 3.11, 3.12, and 3.13.
Python 3.14 cannot be tested yet (pydantic-core lacks a 3.14 wheel).

Note: the requires-python < 3.14 upper bound is intentionally left unchanged.
Lifting it is a separate decision once the full dependency chain supports 3.14.

Refs #2767

@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. backend Pertains to the Python backend. dependencies Pull requests that update a dependency file labels Jun 8, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 4 files

Re-trigger cubic

@pidefrem pidefrem changed the title fix(cli): remove nest_asyncio to restore Python 3.14 compatibility fix(cli,session): remove nest_asyncio, make chat-profile config async-safe Jun 8, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread cypress.config.ts Outdated
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Jun 9, 2026
@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open for 14 days with no activity.

@github-actions github-actions Bot added the stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed label Jun 24, 2026
@pidefrem

Copy link
Copy Markdown
Author

Not stale — this PR is active. I just pushed 012f46d1 (docstring clarification for resolve_config caching), and the branch is up to date with main with no conflicts. It fixes #2952.

It's been waiting on a maintainer review. Could someone take a look when they have a chance? Happy to rebase or address feedback. Thanks! 🙏

@github-actions github-actions Bot removed the stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed label Jun 27, 2026

@dokterbob dokterbob left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Some much welcomed cleanup/fixup.

@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open for 14 days with no activity.

@github-actions github-actions Bot added the stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed label Jul 15, 2026
auto-merge was automatically disabled July 15, 2026 13:53

Head branch was pushed to by a user without write access

@pidefrem
pidefrem force-pushed the fix/remove-nest-asyncio-python-3-14 branch 2 times, most recently from 6aec8da to dbda012 Compare July 15, 2026 14:05
pidefrem added 3 commits July 15, 2026 16:06
nest_asyncio.apply() was called at import time in chainlit/cli/__init__.py.
nest_asyncio <= 1.6.0 internally calls asyncio.ensure_future(future, loop=self),
where the loop= keyword was removed in Python 3.14 (bpo-39529). This corrupts
asyncio task registration, causing asyncio.current_task() to return None inside
running coroutines — which surfaced as a white page (HTTP 500 on every static
asset) on every Chainlit app running on Python 3.14.

asyncio.run(start()) is a top-level entry point and does not need re-entrant
loop support, so nest_asyncio is removed entirely, along with the nest-asyncio
dependency and its mypy override. A regression test guards against silent
reintroduction.

Refs Chainlit#2767
WebsocketSession.get_config() called asyncio.get_event_loop().run_until_complete()
from within the already-running event loop (the SocketIO connect handler). This
only worked because nest_asyncio patched run_until_complete to be re-entrant;
removing nest_asyncio would silently break chat-profile config overrides on all
Python versions.

get_config() now returns the cached config immediately, and a new async
resolve_config() properly awaits set_chat_profiles(). The connect() handler
calls await session.resolve_config() after construction. Adds hatchling to the
mypy optional deps (it ships py.typed; fixes a pre-existing import-not-found
error in build.py). New tests cover overrides, idempotency, error handling,
unknown profiles, and a regression test verifying run_until_complete is never
called.
Kill the entire Chainlit process group (uv -> chainlit -> uvicorn) between
specs and poll until the port is free, resolving an EADDRINUSE race in
waitForPortFree. Exclude .pytest_cache from prettier checks.

Also ensure the Cypress binary is present before tests run (a restored-but-
incomplete cache, seen on windows-latest, leaves the npm package installed
while the binary is missing) and set fail-fast: false so one shard's flake no
longer cancels the rest of the matrix.
@pidefrem
pidefrem force-pushed the fix/remove-nest-asyncio-python-3-14 branch from dbda012 to 42058b9 Compare July 15, 2026 14:06
@github-actions github-actions Bot removed the stale Issue has not had recent activity or appears to be solved. Stale issues will be automatically closed label Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Pertains to the Python backend. dependencies Pull requests that update a dependency file size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants