Using Streamlit with MQTT - Forced multithreading

Hello all,

I’m trying to use Streamlit with Paho MQTT. When receiving a message, my MQTT client fires on_message(), and I would like this function to pop a st.success(“Received message!”) and store the value of the message somewhere.

The issue: on_message() is a callback executed on another thread created when initializing the MQTT client (I pretty much can’t do anything about it), which means streamlit functions can’t be called in it.

I’ve tried using add_script_run_ctx(mqtt_client._thread, get_script_run_ctx)), but it raises RuntimeError: FragmentThreadState not initialized - ScriptRunContext.reset() or add_script_run_ctx must be called on this thread first. I can’t use st.session_state as it remains unavailable, nor a global variable since it would get reset everytime Streamlit tries to get updated.

Thank you in advance

Welcome to the Streamlit community! :balloon: This is a classic challenge—you’re not alone! Streamlit’s architecture doesn’t support calling Streamlit functions (like st.success or updating st.session_state) from threads outside the main script thread, which is exactly what happens with Paho MQTT’s on_message callback. Even using add_script_run_ctx on the MQTT thread won’t work reliably, as Streamlit’s context management is not designed for this and will raise the error you saw. This is a well-documented limitation and not something you can work around with public APIs as of now. See the official docs and community discussions for details: Streamlit Multithreading Guide, MQTT + Streamlit forum thread, and Streamlit with Paho MQTT is not possible.

The recommended workaround is to have your MQTT thread write incoming messages to an external queue (like queue.Queue or a file/database), and then have your Streamlit app poll this queue on each rerun (e.g., using st.button, st.experimental_rerun, or a timer/auto-refresh pattern). This way, all Streamlit UI updates and state changes happen in the main thread, avoiding context errors. Direct, real-time UI updates from the MQTT callback thread are not supported by Streamlit’s public API. If you want to share your code or a minimal reproducible example, the community can help you adapt it! :snake::sparkles:

Sources: