The error appears while running an asynchronous chronometer controlled by a Streamlit interface. The session state variables are failed to be used on the deployed version on Streamlit Cloud, while the same code works perfectly fine locally.
Reproducible Code Example
The repository with the full reproducible code and the used requirements.txt
file can be found here.
import asyncio
import time
import streamlit as st
async def run_chronometer(container):
run_time = 0.0
while st.session_state["chrono_running"]:
st.session_state["chrono_stop_ts"] = time.time()
run_time = (
st.session_state["chrono_stop_ts"] - st.session_state["chrono_start_ts"]
)
container.subheader(f"{run_time:.2f}")
await asyncio.sleep(0.01)
def init_chrono():
st.session_state["chrono_running"] = False
st.session_state["chrono_start_ts"] = None
st.session_state["chrono_stop_ts"] = None
if "chrono_running" not in st.session_state:
init_chrono()
st.header("Chronometer")
col1_run, col2_run = st.columns(2)
container = st.empty()
col1_save, col2_save = st.columns(2)
# buttons to run chronometer
with col1_run:
if st.button("Start", use_container_width=True):
st.session_state["chrono_start_ts"] = time.time()
st.session_state["chrono_running"] = True
st.session_state["chrono_stop_ts"] = None
with col2_run:
if st.button("Stop", use_container_width=True):
st.session_state["chrono_running"] = False
# fill container when chrono is not running
if not st.session_state["chrono_running"]:
if not st.session_state["chrono_stop_ts"]:
container.subheader(f"{0.0:.2f}")
else:
run_time = round(
st.session_state.chrono_stop_ts - st.session_state.chrono_start_ts, 2
)
container.subheader(f"{run_time:.2f}")
# buttons to save chronometer result
with col1_save:
if st.button(
"Save",
disabled=(not st.session_state["chrono_stop_ts"]),
use_container_width=True,
):
st.write("Saved time:", run_time)
with col2_save:
if st.button(
"Delete",
disabled=(not st.session_state["chrono_stop_ts"]),
use_container_width=True,
):
init_chrono()
st.experimental_rerun()
# run chronometer asynchronously
asyncio.run(run_chronometer(container))
Steps To Reproduce
Go to the deployed version.
Then follow these steps:
- Click on “Start” button
- Click on “Stop” button
- Click on “Save” or “Delete” buttons
- Click on “Start” button again
- Click on “Stop” button again
The following error message will pop up:
Traceback (most recent call last):
File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 565, in _run_script
exec(code, module.__dict__)
File "/app/streamlit-chronometer/chronometer.py", line 78, in <module>
asyncio.run(run_chronometer(container))
File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
return future.result()
File "/app/streamlit-chronometer/chronometer.py", line 14, in run_chronometer
while st.session_state["chrono_running"] and run_time < MAX_TIME - TIMESTEP_REAL:
File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/state/session_state_proxy.py", line 90, in __getitem__
return get_session_state()[key]
File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/state/safe_session_state.py", line 104, in __getitem__
raise KeyError(key)
KeyError: 'chrono_running'
And the stop time will always fail to be captured from now on.
Debug info
- Streamlit version: 1.19
- Python version: 3.10
- Operating System: Windows
Additional Comments
If you try to use streamlit==1.20+
, you would obtain the same error locally as well. I found out this is due to the fastReruns
default value which has become true
in these releases. If you overwrite the value to false
within .streamlit/config.toml
file, you would solve the problem locally but it will persist on the deployed version. Using streamlit==1.19
overcomes this problem.