Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

_subplots' or _redrawFromAutoMarginCount undefined using dcc.Loading and dcc.graph #672

Closed
renaudl opened this issue Oct 7, 2019 · 12 comments
Assignees
Labels
dash-type-bug Something isn't working as intended
Milestone

Comments

@renaudl
Copy link

renaudl commented Oct 7, 2019

Hi All,

I have console error message when the graph loads. With a simple scenario as code shows below, I get this error:

plotly-1.49.4.min.js:7 Uncaught (in promise) TypeError: Cannot read property '_subplots' of undefined
at plotly-1.49.4.min.js:7
at Object.r.drawFramework (plotly-1.49.4.min.js:7)
at e (plotly-1.49.4.min.js:7)
at Object.l.syncOrAsync (plotly-1.49.4.min.js:7)
at i (plotly-1.49.4.min.js:7)

if I set the figure to {} or to {layout:{}} then I get a warning (which is good) and the following error:
plotly-1.49.4.min.js:7 Uncaught (in promise) TypeError: Cannot read property '_redrawFromAutoMarginCount' of undefined
at C (plotly-1.49.4.min.js:7)
at plotly-1.49.4.min.js:7

Here sample code:

@app.callback(Output('my-graph-2', 'figure'),
              [Input('my-dropdown', 'value')])
def update_graph(value):

    values = [1,2,3]
    range = [1,2,3]

    return {
        'data': [{
            'x': range ,
            'y': values,
            'line': {
                'width': 1,
                'shape': 'spline'
            }
        }],
        'layout': {
            'margin': {
                'l': 30,
                'r': 20,
                'b': 30,
                't': 20
            }
        },

    }


def layout(unresolved_path=None, resolved_path=None):
    return html.Div([
            dcc.Dropdown(
                id='my-dropdown',
                options=[
                    {'label': 'option1', 'value': 'option1'},
                    {'label': 'option2', 'value': 'option2'},
                ],
                value= 'option1'
            ),
            dcc.Loading(id="loading-chart",
                        children=[dcc.Graph(id='my-graph-2')],
                        type="circle")

        ])
@alexcjohnson
Copy link
Collaborator

@renaudl we've been trying to pin down this _subplots bug for weeks now (plotly/plotly.js#4157) but nobody internally here has managed to reproduce it. If I try to turn your example code into a standalone app I don't see the bug. Does this produce the bug for you? If so, can you tell us about your system - dash versions, browser and version, OS? And if not, any help you can give finding a complete standalone app that does show this bug would be very much appreciated!

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash(__name__)

app.layout = html.Div([
    dcc.Dropdown(
        id='my-dropdown',
        options=[
            {'label': 'option1', 'value': 'option1'},
            {'label': 'option2', 'value': 'option2'},
        ],
        value='option1'
    ),
    dcc.Loading(
        id="loading-chart",
        children=[dcc.Graph(id='my-graph-2')],
        type="circle"
    )

])


@app.callback(Output('my-graph-2', 'figure'),
              [Input('my-dropdown', 'value')])
def update_graph(value):
    values = [1, 2, 3]
    range = [1, 2, 3]
    return {
        'data': [{
            'x': range,
            'y': values,
            'line': {'width': 1, 'shape': 'spline'}
        }],
        'layout': {
            'margin': {'l': 30, 'r': 20, 'b': 30, 't': 20}
        }
    }


if __name__ == '__main__':
    app.run_server(debug=True)

@renaudl
Copy link
Author

renaudl commented Oct 8, 2019

Will do, next friday. Thanks.

@etpinard
Copy link

What could help us also would be to use an un-minified version of plotly.js. I'm not sure if there's an easy way to do so from dash though.

@renaudl
Copy link
Author

renaudl commented Oct 16, 2019

Here you go. The error starts to appear when adding the dcc.location and corresponding layout (requiring suppress_callback_exceptions=True).

version are:
dash 1.3.0
dash-bootstrap-components 0.7.1
dash-core-components 1.2.0
dash-html-components 1.0.1
dash-renderer 1.1.0
dash-table 4.3.0

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash('app',
suppress_callback_exceptions=True)
app.scripts.config.serve_locally = True

def get_layout():
return html.Div(
[
html.H1('subplot issue'),
dcc.Location(id='url', refresh=False),
dcc.Loading(id="page-content")
]
)

@app.callback(Output('page-content', 'children'),
[Input('url', 'value')])
def render_page(url):
return html.Div([
dcc.Loading([
dcc.Dropdown(
id='my-dropdown',
options=[
{'label': 'option 1', 'value': '1'},
{'label': 'option 2', 'value': '2'}
],
value='1'
),
dcc.Graph(id='my-graph')],
type='circle')
]
)

app.layout = get_layout

@app.callback(Output('my-graph', 'figure'),
[Input('my-dropdown', 'value')])
def update_graph(value):

values = [1, 2, 3]
ranges = [1, 2, 3]

return {
    'data': [{
        'x': ranges,
        'y': values,
        'line': {
            'shape': 'spline'
        }
    }],
}

if name == 'main':
app.run_server()

@etpinard
Copy link

This problem is most likely fixed in plotly.js v1.50.1 via plotly/plotly.js#4269

@Marc-Andre-Rivet
Copy link
Contributor

Marc-Andre-Rivet commented Oct 16, 2019

Testing this with both 1.50.0 and 1.50.1, I get the same errors as described above for both versions using:

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash('app',
    suppress_callback_exceptions=True)
app.scripts.config.serve_locally = True

def get_layout():
    return html.Div([
        html.H1('subplot issue'),
        dcc.Location(id='url', refresh=False),
        dcc.Loading(id="page-content")
    ])

@app.callback(Output('page-content', 'children'), [Input('url', 'value')])
def render_page(url):
    return [
        dcc.Dropdown(
            id='my-dropdown',
            options=[
                {'label': 'option 1', 'value': '1'},
                {'label': 'option 2', 'value': '2'}
            ],
            value='1'
        ),
        dcc.Graph(id='my-graph')
    ]

app.layout = get_layout

@app.callback(Output('my-graph', 'figure'), [Input('my-dropdown', 'value')])
def update_graph(value):
    values = [1, 2, 3]
    ranges = [1, 2, 3]

    return {
        'data': [{
            'x': ranges,
            'y': values,
            'line': {
                'shape': 'spline'
            }
        }],
    }

if __name__ == "__main__":
    app.run_server(debug=True)

Modifying usage of dcc.Loading to match typical usage does not trigger an error:

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash('app',
    suppress_callback_exceptions=True)
app.scripts.config.serve_locally = True

def get_layout():
    return html.Div([
        html.H1('subplot issue'),
        dcc.Location(id='url', refresh=False),
        dcc.Loading(
            id="page-content",
            children=[
                dcc.Dropdown(
                    id='my-dropdown',
                    options=[
                        {'label': 'option 1', 'value': '1'},
                        {'label': 'option 2', 'value': '2'}
                    ],
                    value='1'
                ),
                dcc.Graph(id='my-graph')
            ],
            type='circle'
        )
    ])

app.layout = get_layout

@app.callback(Output('my-graph', 'figure'), [Input('my-dropdown', 'value')])
def update_graph(value):
    values = [1, 2, 3]
    ranges = [1, 2, 3]

    return {
        'data': [{
            'x': ranges,
            'y': values,
            'line': {
                'shape': 'spline'
            }
        }],
    }

if __name__ == "__main__":
    app.run_server(debug=True)

Substituting dcc.Loading from the first example for html.Div the error disappears, setting the children directly in the dcc.Loading component, the error also disappears, so this is specifically a dcc.Loading component issue, on a children callback.

Logging the Plotly.XYZ calls from dcc.Graph in both cases shows a totally different picture. Specifically, the div-wrapped graph creates one instance and renders once. The loading-wrapped graph creates two instances, unmounts one and renders both.

The error seems to be in dcc, this promise (https://github.com/plotly/dash-core-components/blob/dev/src/components/Graph.react.js#L93) gets cut short and aborts when the 1st component is unmounted, causing an unploted graph to be purged -- adding a if(this._hasPlotted) check around the Plotly.purge call resolves the error from dcc's perspective, with the graph rendered correctly in the end, if with one more rendering cycle.

This might be indicative of a deeper rendering problem within the loading component - why are we creating the graph twice?

@alexcjohnson @etpinard Will need confirmation as to whether the above diagnostic makes sense from Plotlyjs' perspective.

@alexcjohnson
Copy link
Collaborator

Nice detective work @Marc-Andre-Rivet - now we're getting somewhere!

adding a if(this._hasPlotted) check around the Plotly.purge call resolves the error from dcc's perspective

If you mean Plotly.purge itself is throwing the error, that seems like a problem, I'd say you should be able to purge anything as many times as you want without error, and the fix should be in plotly.js.

If you mean Plotly.purge is acting on something it shouldn't act on, and therefore later we have an error, then yes we should definitely patch that here in dcc.

Seems worth tracking down the double-rendering issue with dcc.Loading as well, but that can be treated as a separate issue.

@Marc-Andre-Rivet
Copy link
Contributor

From what I can tell, Plotly.purge itself is throwing an error when processing a DOM element that hasn't been used to plot something.

@Marc-Andre-Rivet
Copy link
Contributor

Seems worth tracking down the double-rendering issue with dcc.Loading as well, but that can be treated as a separate issue.

#684

@alexcjohnson
Copy link
Collaborator

Hmm OK, let's protect the Plotly.purge call at the dcc level for now, and then see if we can reduce this problem to straight-up plotly.js calls and file a bug report there. In a quick test I wasn't able to get any errors out of Plotly.purge either repeatedly calling it on the same element or calling it on an untouched <div>.

@Marc-Andre-Rivet
Copy link
Contributor

Maybe this happens because the element does not exist anymore by the time the purge gets to it (unmount)? The stack indicated this was happening in a promise.

@Marc-Andre-Rivet Marc-Andre-Rivet self-assigned this Oct 16, 2019
@Marc-Andre-Rivet Marc-Andre-Rivet added this to the Dash v1.5.0 milestone Oct 16, 2019
@Marc-Andre-Rivet Marc-Andre-Rivet added the dash-type-bug Something isn't working as intended label Oct 16, 2019
@Marc-Andre-Rivet
Copy link
Contributor

Closing as fixed by #681

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
dash-type-bug Something isn't working as intended
Projects
None yet
Development

No branches or pull requests

4 participants