Cannot find "active_app"
While running a Textual app with its run_async() method, I get this message in the Textual console:
Task exception was never retrieved
future: <Task finished name='set_timer#1' coro=<Timer._run_timer() done, defined at
/home/david/git/textual/src/textual/timer.py:137> exception=LookupError(<ContextVar
name='active_app' at 0x7f64d1c924d0>)>
Traceback (most recent call last):
File "/home/david/git/textual/src/textual/timer.py", line 140, in _run_timer
await self._run()
File "/home/david/git/textual/src/textual/timer.py", line 169, in _run
await self._tick(next_timer=next_timer, count=count)
File "/home/david/git/textual/src/textual/timer.py", line 176, in _tick
app = active_app.get()
LookupError: <ContextVar name='active_app' at 0x7f64d1c924d0>
Sometimes it makes the app crash, sometimes not. I don't have a reproducible example because it happens in a bigger application, but do you know what could cause this?
# Textual Diagnostics
## Versions
| Name | Value |
|---------|--------|
| Textual | 1.0.0 |
| Rich | 13.9.4 |
## Python
| Name | Value |
|----------------|---------------------------------------------------------------------------|
| Version | 3.13.1 |
| Implementation | CPython |
| Compiler | GCC 13.3.0 |
| Executable | /home/david/.local/share/hatch/env/virtual/jpterm/N1y3coPp/dev/bin/python |
## Operating System
| Name | Value |
|---------|------------------------------------------------------|
| System | Linux |
| Release | 6.1.0-27-amd64 |
| Version | #1 SMP PREEMPT_DYNAMIC Debian 6.1.115-1 (2024-11-01) |
## Terminal
| Name | Value |
|----------------------|-----------------|
| Terminal Application | vscode (1.96.1) |
| TERM | xterm-256color |
| COLORTERM | truecolor |
| FORCE_COLOR | *Not set* |
| NO_COLOR | *Not set* |
## Rich Console options
| Name | Value |
|----------------|---------------------|
| size | width=95, height=45 |
| legacy_windows | False |
| min_width | 1 |
| max_width | 95 |
| is_terminal | True |
| encoding | utf-8 |
| max_height | 45 |
| justify | None |
| overflow | None |
| no_wrap | False |
| highlight | None |
| markup | None |
| height | None |
If you don't have the textual command on your path, you may have forgotten to install the textual-dev package.
Feel free to add screenshots and / or videos. These can be very helpful!
Thank you for your issue. Give us a little time to review it.
PS. You might want to check the FAQ if you haven't done so already.
This is an automated reply, generated by FAQtory
This snippet triggers the error:
import asyncio
from textual.app import App, ComposeResult
from textual.containers import Container
from textual.widgets import MarkdownViewer
EXAMPLE_MARKDOWN = """\
# Markdown Viewer
This is an example of Textual's `MarkdownViewer` widget.
## Features
Markdown syntax and extensions are supported.
- Typography *emphasis*, **strong**, `inline code` etc.
- Headers
- Lists (bullet and ordered)
- Syntax highlighted code blocks
- Tables!
## Tables
Tables are displayed in a DataTable widget.
| Name | Type | Default | Description |
| --------------- | ------ | ------- | ---------------------------------- |
| `show_header` | `bool` | `True` | Show the table header |
| `fixed_rows` | `int` | `0` | Number of fixed rows |
| `fixed_columns` | `int` | `0` | Number of fixed columns |
| `zebra_stripes` | `bool` | `False` | Display alternating colors on rows |
| `header_height` | `int` | `1` | Height of header row |
| `show_cursor` | `bool` | `True` | Show a cell cursor |
"""
class MyApp(App):
def compose(self) -> ComposeResult:
self.container = Container()
yield self.container
async def task(app: MyApp):
await asyncio.sleep(1)
app.container.mount(MarkdownViewer(EXAMPLE_MARKDOWN))
async def main():
app = MyApp()
t0 = asyncio.create_task(task(app))
t1 = asyncio.create_task(app.run_async())
await asyncio.Future()
if __name__ == "__main__":
asyncio.run(main())
Note that it works fine if I replace MarkdownViewer(EXAMPLE_MARKDOWN) with Header() for instance.
One workaround is to:
from textual._context import active_app
And to set the active app before mounting the widget:
active_app.set(app)
app.container.mount(MarkdownViewer(EXAMPLE_MARKDOWN))
But I'm not sure if there's a better solution?
I wouldn't expect the MRE to work. It looks like it would call mount before the app is running. The earliest that should work is when the App handles the Ready event. If you try to do anything with the DOM prior to that, then the results may be undefined.
Not sure if that explains your original issue. Could you be creating a timer before the app is running?
The mount is called after the app is running, but in a different stack, so contextvars are unrelated. That's why setting the active_app before the mount solves the issue.
I am running into this problem when calling await app.recompose(), the workaround @davidbrochart mentioned (active_app.set(app)) works.
For context, I am running into it here https://github.com/basnijholt/tuitorial/pull/18