widget vs session_state() issue??

with col_right:
st.markdown(“### 2. Utility Input Section”)

if st.session_state.pipeline_list:
    node_map = {f"{x['icon']} {x['name']} ({x['p_id']})": x['p_id'] for x in st.session_state.pipeline_list}
    sel_label = st.selectbox("Select Node to Configure", list(node_map.keys()))
    
    # Detect if we switched nodes to clear temporary widget states
    if st.session_state.active_node_id != node_map[sel_label]:
        st.session_state.active_node_id = node_map[sel_label]
        st.rerun()
        
    node = next((x for x in st.session_state.pipeline_list if x["p_id"] == st.session_state.active_node_id), None)
    
    if node:
        st.info(f"Configuring: **{node['name']} ({node['p_id']})**")
        
        # Use a form to batch the "Save" action
        with st.form(key=f"form_{node['p_id']}"):
            for p_n, p_d, p_t, p_c in node['params']:
                v_id = f"{node['p_id']}-{p_n}"
                
                # Sync background state to widget key
                if v_id in st.session_state:
                    st.session_state[f"w_{v_id}"] = st.session_state[v_id]
                
                c_in, c_br = st.columns([4, 1])
                
                # Input Field
                u_val = c_in.text_input(f"{p_n} ({p_d})", key=f"w_{v_id}")
                
                # Browse Facility (Note: Browse triggers rerun, which is fine)
                if p_t in ["Folder", "File", "CSV", "TXT"]:
                    if c_br.markdown(f'<div style="padding-top:25px;"></div>', unsafe_allow_html=True) or True: # Spacer
                        if st.form_submit_button(f"Browse {p_n}"):
                            if p_t == "Folder": browse_folder(v_id)
                            else: browse_file(v_id)
                            st.rerun()
                
                # Temporarily track the value
                st.session_state[v_id] = u_val

            # THE SAVE BUTTON
            if st.form_submit_button("✅ Save & Lock Configuration", width="stretch"):
                for p_n, _, _, _ in node['params']:
                    key = f"{node['p_id']}-{p_n}"
                    node["values"][key] = st.session_state.get(key, "")
                st.success(f"Configuration for {node['p_id']} Saved Successfully!")

        # Live Verification Table
        st.markdown("---")
        st.table(pd.DataFrame([{"Variable": k, "Value": v} for k, v in node["values"].items()]))

Hey, thanks for sharing your code and welcome to the Streamlit community! :balloon: Your approach is solid, but there are a few Streamlit state and rerun nuances to be aware of, especially with forms, selectboxes, and session state updates.

Key points:

  • When you update st.session_state (like active_node_id) and call st.rerun(), the script restarts, so any code after st.rerun() in that run won’t execute (see docs).
  • Widget values should be managed via their key in st.session_state. Avoid directly setting widget keys after the widget is rendered in the same run, as this can cause StreamlitAPIException (see docs).
  • For forms, only the submit button can have a callback; all widget values are sent to the backend only on submit (see docs).

Suggestions:

  • To clear or reset widget values when switching nodes, update the widget keys or use unique keys per node.
  • Avoid setting st.session_state[f"w_{v_id}"] after the widget is rendered; instead, initialize it before rendering the widget.
  • Use the key parameter for all widgets and access their values via st.session_state.

Would you like a step-by-step refactor or a minimal reproducible example? If you can share a small, runnable snippet, the community can help debug even faster!

Sources: