Skip to content

[BUG] ctx.triggered_id is not recognized as a dict #3968

Open

Description

@ProfessorOwl

Describe your context
Please provide us your environment, so we can easily reproduce the issue.

dash                      4.4.1
dash_ag_grid              35.3.0
dash-extensions           2.0.6
dash-iconify              0.1.2
dash_mantine_components   2.8.0

Describe the bug

I am defining multiple dynamic components and set their id like this:

id={"type": "entry", "index": id},

where the value of "index" gets changed depending on the component.
Now, when I want to use the id in a pattern-matching callback, I need to get the value of "index"to search a sqlite database. When I use

ctx.triggered_id.get("index"))

however, VS Code produces an error and says: ""get" is no known attribute of "None"". The call however correctly returns the value of "index"

The same problem happens with ctx.triggered_id["index"], although the error then says: "None" object is not subscriptable.

FYI: I translated the error messages from German to English. They could be slightly inaccurate, though their meaning should be correct

Expected behavior

I would expect it to see a dict as a possible type of triggered_id, so that there is no error shown.

Activity

  1. ProfessorOwl commented on Sep 1, 2026

    @ProfessorOwl
    Author

    The same problem also seems to exist with ctx.outputs_grouping.
    I am defining the outputs like this:

           output={
                "drawer": Output("drawer", "opened"),
                "entries": {
                    "name": Output("entry_name", "value"),
                    "amount": Output("entry_amount", "value"),
                    "paid": Output("entry_paid", "checked"),
                    "category": Output("entry_category", "value"),
                    "date": Output("entry_date", "value"),
                    "interval": Output("entry_interval", "value"),
                    "description": Output("entry_description", "value"),
                },
            },

    This gives the following error when calling ctx.outputs_grouping["entries"]

    no overloads for "__getitem__" match the provided arguments
    

    The function .get() is also not recognized as a valid function.

    Both ["entries"] and .get() work, nonetheless

  2. AnnMarieW commented on Sep 1, 2026

    @AnnMarieW
    Collaborator

    @ProfessorOwl

    Could you please include a minimal example to replicate the issue? Here is one that show that ctx.triggered_id can be None when the callback first runs:

    from dash import Dash, html, Input, Output, ALL, ctx
    
    app = Dash()
    
    app.layout = html.Div([
        html.Button("Entry 1", id={"type": "entry", "index": 1}),
        html.Button("Entry 1", id={"type": "entry", "index": 1}),
        html.Div(id="out")
    ])
    
    
    @app.callback(
        Output("out", "children"),
        Input({"type": "entry", "index": ALL}, "n_clicks"),
        #prevent_initial_call=True,
    )
    def update_entries(n_clicks):
        return f"n: {n_clicks},    ctx: {ctx.triggered_id}"
    
    
    if __name__ == "__main__":
        app.run(debug=True)
    
  3. ProfessorOwl commented on Sep 2, 2026

    @ProfessorOwl
    Author

    HI @AnnMarieW, sorry, I should have done that from the beginning.
    If I modify your example and put the .get function and ["index"] subscript behind ctx.triggered_id, then Pylance displays an error (reportOptionalMemberAccess for .get() and reportOptionalSubscript for ["index"]. I now see that if I test beforehand that ctx.triggered_idis not None, then the errors disappear. But since prevent_initial_call=True is set, only inputs with a dict as an id can trigger the callback, so the error is obsolete. But I can understand now that it can be argumented that – for type safety – there should be an additional check in place, independent from prevent_initial_call.

    from dash import Dash, html, Input, Output, ALL, ctx
    from dash.exceptions import PreventUpdate
    
    
    app = Dash()
    
    app.layout = html.Div(
        [
            html.Button("Entry 1", id={"type": "entry", "index": 1}),
            html.Button("Entry 1", id={"type": "entry", "index": 2}),
            html.Div(id="out"),
        ]
    )
    
    
    @app.callback(
        Output("out", "children"),
        Input({"type": "entry", "index": ALL}, "n_clicks"),
        prevent_initial_call=True,
    )
    def update_entries(n_clicks):
        # if ctx.triggered_id is None:
        #     raise PreventUpdate
        return f"n: {n_clicks},    ctx.get: {ctx.triggered_id.get("index")},    ctx.subscript: {ctx.triggered_id["index"]}"
    
    if __name__ == "__main__":
        app.run(debug=True)

    The error regarding ctx.outputs_grouping can also be circumvented by checking if isinstance(ctx.outputs_grouping, list), but since the outputs are statically written out as a dict, it would be nice if that could be somehow given over to Pylance. The issue Pylance reports is reportAttributeAccessIssue

    Here is a MWE:

    from dash import Dash, html, Input, Output, ALL, ctx
    from dash.exceptions import PreventUpdate
    
    app = Dash()
    
    app.layout = html.Div(
        [
            html.Button("Entry 1", id={"type": "entry", "index": 1}),
            html.Button("Entry 1", id={"type": "entry", "index": 2}),
            html.Div(id="out"),
        ]
    )
    
    
    @app.callback(
        output={"div1": Output("out", "children")},
        inputs=Input({"type": "entry", "index": ALL}, "n_clicks"),
        prevent_initial_call=True,
    )
    def update_entries(n_clicks):
        # if isinstance(ctx.outputs_grouping, list):
        #     raise PreventUpdate
        return {key: f"n: {n_clicks}" for key in ctx.outputs_grouping.keys()}
    
    
    if __name__ == "__main__":
        app.run(debug=True)

    I understand now that the type problems can be mitigated by extensive type checking. If this is too extensive to implement, then I fully understand, though it is cumbersome to have all these lines of typechecking in place, where the types are pretty much hardcoded.

  4. camdecoster commented on Sep 2, 2026

    @camdecoster
    Contributor

    I'm inclined to leave the types as is, but I'll leave this open for now to see if anyone else would like to see a change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions