Mutipages and st.session_state has no key "username"

Summary

KeyError: ‘st.session_state has no key “username”. Did you forget to initialize it? More info: Add statefulness to apps - Streamlit Docs

Steps to reproduce

i create a mutipage with treamlit_option_menu. it have three pages is as follow:tax,test,xm
i succed to login the main page,but when i swith the other page ,for exampe tax page,it error
st.session_state has no key “username”.
Code snippet:

import streamlit as st
from streamlit_option_menu import option_menu
from menuPages import tax,test,xm
st.set_page_config(
    page_title="菜单与登陆相结合",
    layout="wide",)
st.markdown(
    """
    <style>
        .css-1oe5cao {display: none;}
       
    </style> 
    """, unsafe_allow_html=True
)

def menu():
    #主程序-带菜单
    v_menu=["tax","test","xm"]
    with st.sidebar:
        st.header("main form")
        selected=option_menu(
            menu_title=None,
            options=v_menu,
            icons=None,menu_icon="menu-down",default_index=0,)
        if selected=="tax":
            tax.createPage()
        if selected=="test":
            test.createPage()
        if selected=="xm":
            xm.createPage()


def check_password()->tuple:
    """Returns `True` if the user had a correct password."""

    def password_entered()->tuple:
        """Checks whether a password entered by the user is correct."""
        if (
            st.session_state["username"] in st.secrets["passwords"]
            and st.session_state["password"]
            == st.secrets["passwords"][st.session_state["username"]]
        ):
            # del st.session_state["password"]  # don't store username + password
            # del st.session_state["username"]
            return True,st.session_state["username"]
        else:
            return False

    st.title("登陆")
    st.text_input("用户名:", key="username") #相当于将输入的用户名,存到st.session_state["password"]=
    st.text_input("密码:", type="password", key="password")
    if st.button("登陆"):
        if password_entered():
            st.session_state.password_correct = True
            st.experimental_rerun()
        else:
            st.error("😕 错误的用户名或密码")
    return True,st.session_state["username"]
# Streamlit application
# Streamlit application
def main():
    if "password_correct" not in st.session_state:
        st.session_state.password_correct = False
    if st.session_state["password_correct"]==False:
        status,username=check_password()
    else:
        st.title("main page")
        col1,col2=st.sidebar.columns(2)
        with col1:
                st.write("当前用户:"+st.session_state["username"])
        with col2:
                st.button("注销")
        menu()

if __name__ == "__main__":
    main()

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

Expected behavior:

File "D:\python\web\lib\site-packages\streamlit\runtime\scriptrunner\script_runner.py", line 552, in _run_script
    exec(code, module.__dict__)File "D:\python\web\b.py", line 76, in <module>
    main()File "D:\python\web\b.py", line 70, in main
    st.write("当前用户:"+st.session_state["username"])File "D:\python\web\lib\site-packages\streamlit\runtime\state\session_state_proxy.py", line 90, in __getitem__
    return get_session_state()[key]File "D:\python\web\lib\site-packages\streamlit\runtime\state\safe_session_state.py", line 113, in __getitem__
    return self._state[key]File "D:\python\web\lib\site-packages\streamlit\runtime\state\session_state.py", line 378, in __getitem__
    raise KeyError(_missing_key_error_message(key))

Hi @hillstone,

Thanks for posting!

Streamlit throws a handy exception if an uninitialized variable is accessed.

Check if the “username” key exists in st.session_state at the top of your main function and if not, initialize it:

if "username" not in st.session_state:
     st.session_state["username"] = ""

You can also try returning an empty string if “username” does not exists to avoid the KeyError:

with col1:
    st.write("当前用户:"+st.session_state.get("username", ""))

Let me know if any of these work.

thanks.

     Sorry, apparently you didn't look at my docs, just read the error messages. 
     Obviously the login has been successful, and the st.sesssion_state ["username"] has a result, the menu function can be executed. 
     The problem is that this error is reported when the menu function is executed. Can't a session be shared across multiple pages? It's weird. 
      I want the three pages in the menu: tax, test and xm, to share the session-state ["username"] saved by the login page, apparently, this error indicates that the session is not shared. It's weird. 
     How do I set a session to be shared by multiple pages?

     I suggest you test the code yourself. In fact, my idea is very simple, that is, I want to make a login page, after successful login, enter multiple pages made with streamlit-option-menu, and show a logout button on three pages.

There is an important detail to understand about assigning keys to widgets: when the widget is not rendered, all of its data (including its key and value in session state) are deleted.

You can use a pattern to copy data for widgets to other keys (not directly associated to a widget) in order to keep that data across multiple pages or through conditional rendering of various widgets.

Here’s a post where I give an example of this pattern. I prefix keys with underscores when assigning them to widgets and copy the data over to other keys (without the underscore) for keeping, independent of the widget.

Alternatively, there is a hacky trick you can use to interrupt the widget cleanup process. If you have:

if 'my_key' in st.session_state:
    st.session_state.my_key = st.session_state.my_key

at the top of every page where ‘my_key’ is assigned to some widget, this will prevent that widget’s key from getting deleted.

Important: I say to put this at the top of every page because in order to interrupt the key deletion, it needs to be executed on the first script (re)run when the widget is no longer rendered. So if you have a widget on page A and navigate to page B, the hacky script would have to be on page B.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.