Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bad message format: setIn cannot be called on an ElementNode #3223

Closed
snehankekre opened this issue May 6, 2021 · 14 comments
Closed

Bad message format: setIn cannot be called on an ElementNode #3223

snehankekre opened this issue May 6, 2021 · 14 comments
Labels
type:bug Something isn't working

Comments

@snehankekre
Copy link
Collaborator

Summary

Running the below app and clicking on a button to delete entries as shown in the gif causes the following browser alert to be displayed: Bad message format: 'setIn' cannot be called on an ElementNode

Steps to reproduce

Run the app and delete an entry in the sidebar, as shown in the below gif.

$ streamlit run https://gist.githubusercontent.com/mihirsamdarshi/4650e8041954e3fb5e629e71beaf40c3/raw/9a9b0817f8c09ca4529dc41210f3c6e37fe567fe/streamlit_set_in_bug.py

steps

Code snippet:

import streamlit as st
import SessionState

def display_options(session_state):
    """
    Handles loading and editing of DataFrame
            Parameters:
                    session_state (dict): Streamlit SessionState in order to maintain state of dictionary
            Returns:
                    out_value = (str): str
    """
    # load default database
    out_value = 'bananas'

    st.write("#### Choose a value from the list below:")
    session_state.str_list = ['apples', 'oranges', 'grapes']
    # create a container for str selection
    selection_container = st.empty()
    # display database selection
    selected_db = selection_container.radio("", ['Default'] + session_state.str_list)
    # create a message container
    message_placeholder = st.empty()

    # load selected str if not default
    if selected_db != 'Default':
        with st.spinner("Loading..."):
            out_value = selected_db
        message_placeholder.success('Loaded {} successfully'.format(selected_db))

    edit_mode_placeholder = st.empty()
    # edit mode button
    edit_db_mode = edit_mode_placeholder.button('Edit list')
    if edit_db_mode:
        session_state.edit_mode = True

    # enter edit mode
    if session_state.edit_mode:
        message_placeholder.empty()
        edited_selected_str = selection_container.radio("You are editing the list. "
                                                        "Click the exit button twice to exit. "
                                                        "Ignore any \"Bad Message Format\" warnings that may pop up. ",
                                                        session_state.str_list)

        # create a container for edit mode controls
        edit_mode_controls = edit_mode_placeholder.beta_container()
        edit_col1, edit_col2 = edit_mode_controls.beta_columns(2)

        # exit edit mode controls
        exit_edit_mode = edit_col2.button('Exit edit mode')
        if exit_edit_mode:
            session_state.edit_mode = False
        # open delete confirmation dialog
        delete_init = edit_col1.button('Delete selection')
        if delete_init:
            session_state.delete_init = True

        # if begin delete, confirm before deleting
        if session_state.delete_init:
            # clear the container before doing stuff
            edit_mode_placeholder.empty()
            # create a new container in the previous one's place
            delete_mode_controls = edit_mode_placeholder.beta_container()
            # create confirmations
            delete_mode_controls.warning('Are you sure you want to delete the selected database?')
            col1, col2 = delete_mode_controls.beta_columns(2)

            delete_reject = col2.button('No, go back')
            if delete_reject:
                session_state.delete_init = False

            # if confirm delete, then delete str
            delete_confirm = col1.button('Yes, delete it')
            if delete_confirm:
                edited_list = session_state.str_list
                edited_list.remove(edited_selected_str)
                session_state.str_list = edited_list

                # clear the container before doing stuff
                edit_mode_placeholder.empty()
                # create a new container for delete controls
                delete_container = edit_mode_placeholder.beta_container()
                delete_container.success("Delete successful")
                # show go back button with unique key
                exit_edit_mode = delete_container.button('Go back', key='after-delete')
                if exit_edit_mode:
                    session_state.delete_init = False
                    session_state.edit_mode = False

    return out_value


def main():
    st.title('Hello World')
    session_state = SessionState.get(str_list=None, edit_mode=False, delete_init=False)
    with st.sidebar.beta_expander('Options'):
        display_options(session_state)


if __name__ == '__main__':
    main()

Expected behavior:

No alert should be displayed in the browser.

Actual behavior:

Browser alter with the specific message is displayed: Bad message format: 'setIn' cannot be called on an ElementNode

Is this a regression?

no

Debug info

  • Streamlit version: 0.81.1
  • Python version: 3.8.5
  • OS version: Ubuntu 20.04
  • Browser version: Firefox Browser 88.0 (64-bit)

Additional information

Bug first mentioned in the forum: https://discuss.streamlit.io/t/ignore-an-alert-bad-message-format/9913

@snehankekre snehankekre added type:bug Something isn't working status:needs-triage Has not been triaged by the Streamlit team labels May 6, 2021
@inspectorG4dget
Copy link

I faced the same issue while showing a PyDeck map in a pre-allocated space. I was able to circumvent the issue by not using with:

Issue was observed with this code:

with my_space:
    st.pydeck_chart(...PyDeck stuff...)

Issue was circumvented with this code:

my_space.pydeck_chart(...PyDeck stuff...)

@kmcgrady kmcgrady removed the status:needs-triage Has not been triaged by the Streamlit team label May 23, 2021
@kmcgrady
Copy link
Collaborator

Thanks for the issue @snehankekre ! I'm hoping this gets resolved in our updated version of Session State! Marking it to be tested when we release the feature.

@leJOEndary
Copy link

leJOEndary commented Jul 6, 2021

Gentle Bump,

@kmcgrady Just encountered the same issue on Streamlit v0.84.0 using the new native st.session_state

@LukasHaas
Copy link

Same issue here and urgently need a fix. Getting this issue when generating beta_columns on Streamlit v0.84.0.

@leJOEndary
Copy link

So I've fixed it for myself.

The issue in my case was that i was trying to pass a container to the callback so i can display an alert.

The problem is that, the container is not created yet in the context of the new run. (Since the order of execution of when using callbacks calls the callback first thing, then executes the rest of the app).

@kmcgrady
Copy link
Collaborator

kmcgrady commented Jul 7, 2021

Hi @leJOEndary and @LukasHaas Thanks for your insights. I think that lines up. Callbacks were designed to manipulate session state and not change the display of the app. If you would like to display something, you can set a flag in Session State and then update the script to check the flag and display what you need.

def check_valid():
  # where "is not valid" the check you need.
  st.session_state.show_error = st.session_state.text is not valid

st.text_input("Enter text", on_change=check_valid, key="text")

if st.session_state.show_error:
  st.warning("Warning Warning!")

Let me know if that helps. If you have a small sample code example that demonstrates the issue, that would be helpful!

@leJOEndary
Copy link

@kmcgrady Yeah, thank you for the reply. Here's a super simplified version of what I was trying to do which resulted in the exception.

def handle_event(alert_container):
  try:
    # Call an endpoint
    alert_container.success("Your submission has been recorded!")
  except:
    alert_container.warning("Error while submitting, please try again later.")
 

alert_container = st.beta_container()
st.button("Submit", on_click=handle_event, args=(alert_container,))

After i changed it to:

def handle_event():
  try:
    # Call an endpoint
    st.success("Your submission has been recorded!")
  except:
    st.warning("Error while submitting, please try again later.")
 
st.button("Submit", on_click=handle_event)

Things worked again 😄

@jrieke
Copy link
Collaborator

jrieke commented Jul 21, 2023

Closing, this is apparently using the old SessionState hack and seems to be resolved with st.session_state giving the comments above. If it persist, please open a new issue with an updated code example.

@jrieke jrieke closed this as completed Jul 21, 2023
@ibehnam
Copy link

ibehnam commented Sep 3, 2023

This problem still exists. I did something similar to the OP and got the exact same error. I'm on st v.1.26.0

@mtafadar
Copy link

mtafadar commented Sep 8, 2023

As @ibehnam said, this problem still exist and I had similar problem today

@trygvis
Copy link

trygvis commented Dec 28, 2023

@jrieke Can this be reopened? This is still an issue for me, v=1.29.0

@ambroser53
Copy link

This is still an issue on 1.33.0. I get it consistently on the usage of radio selections within an st.empty

@jpparis02
Copy link

This is still an issue, got it today

@MaxwellWibert
Copy link

Got this same issue today

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:bug Something isn't working
Projects
None yet
Development

No branches or pull requests