Download error - .htm error

I have deployed an app which needs to download a ppt after running py code based on user filtering on ui, but it throws .htm error - while i look at at logs i found below:

ERROR:asyncio:Exception in callback AppSession._on_scriptrunner_event..() at /usr/local/lib/python3.12/site-packages/streamlit/runtime/app_session.py:519
 PermissionError: [Errno 13] Permission denied: '/home/app'

anybody would help me fix this - thanks

Can you share your code? Since I see it is an asyncio error, have you reviewed the guide about multithreading in the docs? Threading in Streamlit - Streamlit Docs

hey thanks for your kind response. Let me past my code:

st.session_state.setdefault(“ppt_bytes”, None)

st.session_state.setdefault("show_download", False)

st.session_state.setdefault("is_generating", False)




if st.button("\*\*VIEW DETAILS\*\*", disabled=st.session_state.is_generating, key="btn_generate_ppt"):

 

    \# Optional: clear old bytes BEFORE new generation starts

    st.session_state.ppt_bytes = None

    st.session_state.show_download = True



    if select_value_kpi_checked:

        filters = {"customer_name": selected_customers,

            "value_kpis" : selected_val_kpis}

    else:

        filters = {"customer_name": selected_customers}



    if not selected_customers:

        st.warning("⚠️ Please select customer")



    elif pd.isna(start_date\_) or pd.isna(end_date\_):

        st.warning("⚠️ Please select valid dates")



    elif start_date\_ is None or end_date\_ is None:

        st.warning("⚠️ Please select both start and end dates")



    elif start_date\_ > end_date\_:

        st.error("⚠️ Start date must be less than End date!")



    else:

        logger.info(f"Applying filters.")



        #st.write(df\[\['sum_target','product_actual_usd', 'actual_usd'\]\].dtypes)



        st.write("\*\*CUSTOMER:\*\*  ", selected_customers)



        if not selected_country:

            st.write("\*\*LOCATION:\*\*  ", "All Locations")

        else:

            st.write("\*\*LOCATION:\*\*  ", selected_country)



        st.write("\*\*START DATE:\*\*  ", start_date)

        st.write("\*\*END DATE:\*\*  ", end_date)

        st.write("\*\*CROSS-MAPPING OPTION:\*\*  ", cross_mapping_choice)

        st.write("\*\*KPI OPTION:\*\*  ", kpi_choice)

        



        if select_value_kpi_checked:

            st.write("\*\*VALUE KPIs:\*\*  ", ", ".join(selected_val_kpis))

            filters = {

                "customer_name": selected_customers,

                "country_name": selected_country,

                "value_kpis": selected_val_kpis,

            }

    

        else:

            st.write("\*\*VALUE KPIs:\*\*  ", "All Value KPIs")

            filters = {

                "customer_name": selected_customers,

                "country_name": selected_country,

            }



        logger.info(f"Creating data with filters.")



        filtered_df = apply_filters_old(

            df,

            filters=filters,

            start_date=start_date,

            end_date=end_date,

            current_filtered=None,

            previous_filtered=None,

        )



        full_df_filters = {

                "customer_name": selected_customers,

                "country_name": selected_country,

            }

        

        full_df = apply_filters_old(

            df,

            filters=full_df_filters,

            start_date=start_date,

            end_date=end_date,

            current_filtered=None,

            previous_filtered=None,

        )

        

        logger.info(f"Data created with filters.")



        st.write('\*\*NO. OF RECORDS:\*\*  ', filtered_df.shape\[0\])



        \# if period_on_period_checked:

        if period_on_period_checked and current_year_period and previous_year_period:

            \# logger.info(f"Applying period on period filters.")

            st.write("\*\*PERIOD ON PERIOD:\*\*  Yes")

            st.write(" - Current Period Selected Months: ", ", ".join(current_filtered))

            st.write(" - Previous Period Selected Months: ", ", ".join(previous_filtered))



            filtered_df\_= apply_filters_old(

                df,

                filters=filters,

                start_date=None,

                end_date=None,

                current_filtered=None,

                previous_filtered=None

                )

            

            logger.info(f"Filtering current and previous period dataframes.")

            current_df = filtered_df\_\[

                filtered_df\_\["current_period"\].isin(current_filtered)\]

            

            previous_df = filtered_df\_\[

                filtered_df\_\["previous_period"\].isin(previous_filtered)

                \]

            logger.info(f"Current and previous period dataframes filtered.")

            

            del filtered_df\_



            pop_flag = "yes"

        

        else:

            st.write("\*\*PERIOD ON PERIOD:\*\*  No")



            current_df = None

            previous_df = None

            pop_flag = "no"



        \# logger.info(f"Customer: {selected_customers}")




        \# customer_str = "\_".join(selected_customers)

        

        try:

            st.session_state.is_generating = True

            with st.spinner("⏳ Generating PPT... Please wait"):

                \# time.sleep(5)  # Simulate a delay for PPT generation

                logger.info(f"Generating the report.")

                



                ppt_bytes = export_figures_to_ppt(

                    df_full = full_df, df= filtered_df, df_p1=current_df, df_p2=previous_df, Selected_customer=selected_customers,

                    kpi_pg=kpi_choice, crs_map_pg=cross_mapping_choice, pop_pg=pop_flag,  title_text=None

                    )



                \# Guard against empty output

                if not ppt_bytes:

                    \# raise ValueError("PPT generation produced empty content.")

                    st.warning("⚠️ PPT generation produced empty content.")

                    logger.info(f"PPT generation produced empty content.")



                st.session_state.ppt_bytes = ppt_bytes

                st.session_state.show_download = True



            logger.info(f"Report generated successfully and is ready for download.")

        except Exception as e:

            st.session_state.ppt_bytes = None

            st.session_state.show_download = False

            st.error(f"Failed to generate PPT: {e}")

        finally:

            st.session_state.is_generating = False



    download_clicked = False

    \# if st.session_state.ppt_bytes:

    if st.session_state.show_download and st.session_state.ppt_bytes:

        st.success("PPT is ready to download.")

        \# customer_str = "\_".join(selected_customers)            

        download_clicked = st.download_button(

            label="📥 Download PPT Report",

            data=st.session_state.ppt_bytes,  # \*\*bytes\*\*, not buffer.getvalue() here

            file_name=f"{selected_customers}\_VR_Report.pptx",

            mime="application/vnd.openxmlformats-officedocument.presentationml.presentation",

            

            key="download_ppt",

            )

        st.caption(f"File size: {len(st.session_state.ppt_bytes):,} bytes")




    \# If the user clicked the download button on the last run, hide it now.

    if download_clicked:

        st.session_state.show_download = False

        \# Optional: clear bytes to prevent repeated downloads            

        logger.info(f"File size: {len(st.session_state.ppt_bytes):,} bytes")

        logger.info(f"download successful!")

        st.session_state.ppt_bytes = None

        st.success("Report downloaded. Click 'Generate' again to create a new one.")

I see you have a lot of logging statements. Can you share the full stack trace and log statements, to more quickly identify which line in the app is throwing the error?