Windows fatal exception: stack overflow when callback is triggered

Hello, I am writing a CRUD application that allows the user to fill out information and submit it to the database. In my Submit callback I am getting consistent stack overflow errors when the callback is triggered. I think it may have something to do with using a large amount of dictionaries in one callback but I have no clue how to troubleshoot and mitigate this. I am using Python version 3.7 and Dash version 2.11.1. My callback is below. If someone could help me troubleshoot this issue it would be greatly appreciated.

#Submit Callback
@app.callback(
    Output('hidden-div','children'),
    Input('submit', 'n_clicks'),
    State('browse-path', 'value'),
    State({'group': ALL, 'type': 'input','index': ALL},'id'), #All input IDs (15 Total)
    State({'group': ALL, 'type': 'input','index': ALL},'value'), #All input Values (15 Total)
    State({'group': ALL, 'type': 'drop','index': ALL},'id'), #All dropdown IDs (14 Total)
    State({'group': ALL, 'type': 'drop','index': ALL},'value'), #All dropdown IDs (14 Total-- can contain a list of values)
    State({'type':'textarea', 'index':ALL},'id'), #All text area IDs (4 total)
    State({'type':'textarea', 'index':ALL},'value'), #All text area values (4 total)
    State('Date', 'date'), #Date from singledate picker
    State({'group': 'tbl1','type': 'tbl', 'index': 'Table 1'}, 'data'), #Table 1 data (editable but can contain a max of 8 rows, 6 columns)
    State({'group': 'tbl2','type': 'tbl', 'index': 'Table 2'}, 'data'),  #Table 2  data (editable but can contain a max of 6 rows, 6 columns)
    State('folder-switch', 'value'), #A switch.. can only contain a binary value
    prevent_initial_call=True
)
def submit(n_clicks, folder, iid, ival, ddid, ddval, textareaid,textareaval,
           date,tbl1,tbl2,switch):
    
    
    #zip inputs and dropdowns
    datainputs = {item['index']: value for item, value in zip(iid, ival)}
    ddinputs = {item['index']: value for item, value in zip(ddid, ddval)}
    textarea= {item['index']: value for item, value in zip(textareaid, textareaval)}
    
    #Create Test Table
    testtbl= {
        'Test_ID':datainputs['Test #'],
        'Test_Title':datainputs['Test Title'],
        'Test_Date':date,
        'Test_Type':ddinputs['Test Type'],
        'Test_Summary':textarea['Test Summary'],
        'Anomalies_Observed':textarea['Anomalies Observed'],
        'Other_Observations':textarea['Other Observations'],
        'Test_Limitations':textarea['Test Limitations'],
        'Test_Folder':folder,
        'Analysis_Status':ddinputs['Analysis Status'],
        'Lead_Analyst':ddinputs['Lead Analyst'],
        'Date_Entered':now.strftime('%Y-%m-%d %H:%M:%S'),
        'Date_Edited':now.strftime('%Y-%m-%d %H:%M:%S'),
        'Test_Edited':now.strftime('%Y-%m-%d %H:%M:%S')}
    
    testtbl= pd.DataFrame(testtbl, index=[0])
    
    #Create Table 1 Table
    #Function to convert list of dictionaries to one dictionary
    def merge_list_of_dicts(list_of_dicts):
        merged_dict = defaultdict(list)
        for dictionary in list_of_dicts:
            for key, value in dictionary.items():
                merged_dict[key].append(value)
        return dict(merged_dict)
    
    # tbl1= merge_list_of_dicts(tbl1)
    # tbl1.update({
    #      'Test_ID':testtbl['Test_ID'],
    #      'Date_Entered':now.strftime('%Y-%m-%d %H:%M:%S'),
    #      'Date_Edited':now.strftime('%Y-%m-%d %H:%M:%S'),
    #      })
    
    #tbl1= pd.DataFrame(tbl1, index=[0])
    
    #The 'cleandf' function changes empty white space and NONE data type items in a dataframe to 'NA'
    #It also strips white space from the beginning and start of strings in a dataframe
    def cleandf(df):
        mask = ((df.applymap(type) == str) & (df.applymap(lambda x: str(x).strip() == ''))| df.applymap(pd.isnull))
        df=df.where(~mask, other='NA')
        df=df.apply(lambda x: x.str.strip().str.replace(r'(?i)^n/a$', 'NA', regex=True) if x.dtype == "object" else x) #r'(?i)^n/a$' is a regular expression that captures all variations of 'n/a'
        df = df.apply(lambda x: x.str.strip() if x.dtype == "object" else x)
        return df
    testtbl= cleandf(testtbl)
    
    #Check for valid Test ID and Title
    if testtbl['Test_ID'][0]=='NA':
        print('Bad Test #')
        return None
    if testtbl['Test_Title'][0]=='NA':
        print('Bad Test Title')
        return None

    #Check Table 1
    
    #Updates Sqlite3 Database
    conn = sqlite3.connect(db_file)
    try:
        testtbl.to_sql('Test', conn, if_exists='append', index=False)
        print('Test Table Updated succesfully')
        conn.close()
    except sqlite3.Error as e:
        print(f"SQLite3 Error: {e}")
        conn.close()
        return None
    
    #Create folders
    directory= 'Test'
    path = os.path.join(folder, directory)
    try:
        os.makedirs(path, exist_ok = True)
        print("Directory '%s' created successfully" % directory)
    except OSError as error:
            print(str(error))
            return None

For anyone interested, I solved my problem by setting the recursion limit in my callback: sys.setrecursionlimit(1500) If you do this don’t forget to import sys. Truthfully I do not know what the repercussions of changing the recursion limit so I would not recommend this for production apps unless you know what you are doing. My app is only intended to be used by a small group of people so I do not mind taking the risk.

1 Like