Getting nonexistent object was used error even when suppress_callback_exceptions and prevent_initial_call are set to True

Hi,

Here is an overview of what I am trying to do:

I have a top level tab group which has a few forms.

image

The ‘Load Testbench’ form looks like this:
image

When the user clicks ‘Load’, I want to add a new tab next to the HOME tab.

Here is the code for the layout of where the tab-group is setup:

def banner():
    return dbc.Row(
        [
            dbc.Col(
                [
                    html.H1("Run Dao")
                ],
            ),
        ],
        className="rd-banner align-middle",
    )

def body():
    return dbc.Row(
        [
            dbc.Container(
                [
                    dbc.Card(
                        [
                            dbc.CardHeader(
                                dbc.Tabs(
                                    [
                                        dbc.Tab(
                                            label=" Home",
                                            tab_id="home-tab",
                                            labelClassName=icon_home,
                                        ),
                                    ],
                                    id=get_id("base", "home-tabs"),
                                    active_tab="home-tab",
                                )
                            ),
                            dbc.CardBody(html.P(id="main-content", className="tab-body border")),
                        ]
                    )
                ],
                id="tab-container",
                fluid=True,
            )
        ]
    )

def layout():
    return dbc.Container(
        [
            banner(),
            body(),
        ],
        id="home-layout",
        fluid=True,
    )

@callback(
    Output('main-content', 'children'),
    Input(get_id("base", "home-tabs"), 'active_tab')
)
def display_main_content(active_tab):
    match active_tab:
        case "home-tab":
            return home_tab.layout()

And here is the code of the forms and the callback that I am planning to use to add the new tab in.

def add_card(title, text, icon):
    # Fix up the title for display
    title_fmt = re.sub('-', ' ', title.strip()).title()

    modal = html.Div(
        [
            dbc.Modal(
                [
                    dbc.ModalHeader(dbc.ModalTitle(title_fmt)),
                    dbc.ModalBody(id=get_id("modal-body", title)),
                    dbc.ModalFooter(
                        dbc.Button(
                            "Load",
                            id=get_id("modal-btn-load", title),
                            n_clicks=0
                        )
                    ),
                ],
                id=get_id("modal-top", title),
                is_open=False,
                backdrop='static',
                keyboard=False,
                scrollable=True,
                centered=True,
                size="lg",
            ),
        ]
    )

    return dbc.Col(
        [
            dbc.Card(
                [
                    dbc.CardHeader(html.H5(title_fmt)),
                    dbc.CardBody(
                        [
                            html.P(text, className="card-text"),
                            # The button id is base on the title string
                            dbc.Button(class_name=icon, id=get_id("modal-btn-open", title)),
                            modal,
                        ]
                    )
                ],
            ),
        ],
        width=3,
    )

def layout_home_settings():
    return html.P("Settings not populated yet")

def layout_load_testbench():
    run_path = dbc.Row(
        [
            dbc.Label("Run Path"),
            dbc.Input(
                id=get_id("run-path", "load-testbench"),
                placeholder="/home/",
            ),
            dbc.FormText(
                "Path to the testbench's run/ folder",
                color="secondary",
            ),
            dbc.FormFeedback("Given path doesn't exist", type="invalid"),
        ],
        class_name='g-1 mb-3',
    )

    dash_name = dbc.Row(
        [
            dbc.Label("Testbench Alias"),
            dbc.Input(
                id=get_id("alias", "load-testbench"),
                placeholder=randomname.get_name(),
            ),
            dbc.FormText("A helpful nick name for the TB in the RunDao dashboard"),
        ],
        class_name='g-1 mb-3',
    )

    focus_tb_tab = dbc.Row(
        [
            dbc.Switch(
                id="load-testbench-focus-tab",
                label="Bring the new testbench tab into active focus",
                value=False,
            ),
        ],
        class_name='g-1 mb-3',
    )

    return dbc.Container(
        [
            run_path,
            dash_name,
            # focus_tb_tab,
        ],
    )

def layout():
    return dbc.Container(
        [
            dbc.Row(
                [
                    dbc.CardGroup(
                        [
                            add_card("settings",
                                     "Adjust app wide preferences",
                                     icon_settings
                                    ),
                            add_card("load-testbench",
                                     "Import existing testbench into the dashboard",
                                     icon_import
                                    ),
                        ],
                    ),
                ],
                id="home-tab-row",
                align='center',
                justify='start',
                class_name='g-1',
            ),
        ],
        id="home-tab-container",
        fluid=True,
    )

@callback(
    Output({'type': 'modal-top', 'index': MATCH}, 'is_open'),
    Input({'type': 'modal-btn-open', 'index': MATCH}, 'n_clicks'),
    Input({'type': 'modal-btn-load', 'index': MATCH}, 'n_clicks'),
    State({'type': 'modal-top', 'index': MATCH}, 'is_open'),
)
def toggle_modal(open, load, is_open):
    if open or load:
        return not is_open
    return is_open

@callback(
    Output({'type': 'modal-body', 'index': MATCH}, 'children'),
    Input({'type': 'modal-top', 'index': MATCH}, 'is_open'),
    State({'type': 'modal-top', 'index': MATCH}, 'id'),
)
def modal_body(is_open, id):
    if is_open:
        # remove the component id suffix
        match id['index']:
            case "settings":
                return layout_home_settings()
            case "load-testbench":
                return layout_load_testbench()

# Feedback about whether the given path is accessible or not
@callback(
    Output(get_id("run-path", "load-testbench"), 'valid'),
    Output(get_id("run-path", "load-testbench"), 'invalid'),
    Input(get_id("run-path", "load-testbench"), 'value'),
)
def check_path_exists(path):
    if path:
        valid = os.path.isdir(path)
        return valid, not valid

    return False, False

@callback(
    Output(get_id("base", "home-tabs"), 'children'),
    Input(get_id('modal-btn-load', 'load-testbench'), 'n_clicks'),
    State(get_id('modal-btn-load', 'load-testbench'), 'id'),
    State(get_id("run-path", "load-testbench"), 'valid'),
    State(get_id("run-path", "load-testbench"), 'value'),
    State(get_id("alias", "load-testbench"), 'value'),
    prevent_initial_call=True,
)
def add_new_home_tab(clicked, id, run_path_valid, run_path, alias):
    print(f"{clicked=} {id=} {run_path_valid=} {run_path=} {alias=}")
    ctx = callback_context

    if not ctx.triggered:
        raise PreventUpdate

    print(f"{ctx.__dir__()}")

    raise PreventUpdate

where

def get_id(type, index):
    return {
        'type': type,
        'index': index
    }

As you can see, I am getting the exception for the missing elements from the ‘load-testbench’ arguments.

Can you please help how to avoid this exception?
Or refactor the structure to achieve my objective?

Thank you!