use_theme fails during app.on_startup: “current slot cannot be determined” (no slot/client context)
Summary Calling nicegui_tabulator.use_theme(...) inside a NiceGUI app.on_startup handler raises a RuntimeError: “The current slot cannot be determined because the slot stack for this task is empty.” This appears to happen because startup/background tasks run without an active UI slot/client context, yet use_theme accesses ui.context.client.
- Minimal example:
from nicegui import ui, app
import nicegui_tabulator as tab
@app.on_startup
async def startup():
tab.use_theme('site_dark', shared=True)
@ui.page('/')
def index():
ui.label('Home')
ui.run()
- Start the app and observe the error during startup.
Expected Behavior
- When called with shared=True, use_theme should be safe to call without an active client/slot (e.g., at startup) and apply a global/shared theme, or at least no-op gracefully until a client connects.
Actual Behavior
- Raises at startup with:
- RuntimeError: The current slot cannot be determined because the slot stack for this task is empty. This may happen if you try to create UI from a background task. To fix this, enter the target slot explicitly using with container_element:.
- Trace shows use_theme → ui.context.client access, which requires a current slot.
Analysis
The case shared=None should translate to False.
Also,
if ui.context.client.has_socket_connection:
ui.run_javascript(
"""
const linkElements = document.querySelectorAll('link.nicegui-tabulator-theme');
linkElements.forEach(linkElement => {
linkElement.parentNode.removeChild(linkElement);
});
"""
)
should be removed. It's the user responsability to call it before any table is created.
This would solve the problem.
@AlePiccin, I don’t think we should just remove run_javascript outright. In dynamic theme switching scenarios, we need to ensure old-style links are removed.
Right now, it seems like we have a couple of options:
- Just check if the slot stack is not empty, and run
run_javascript. - Skip
run_javascriptentirely when shared=True.
What do you think?
Hi @CrystalWindSnake. Thanks for the reply. I would go with the second approach. It fits both scenarios:
If the user wants to dynamically change the theme, they should use shared=False (and javascript will run to cleaning it all up).
If the user wants to set it globally, they should use shared=True (in preference inside @app.on_startup)