Rectangular shape randomly appears in `px.imshow` output using `dcc.Graph`

I have a dcc.Graph output component that renders the callback output from px.imshow. Randomly, when I interact with the component, such as drawing a shape or zooming, a random
rectangular shape appears:

Here is how the plot should appear:

But randomly, sometimes this shape will appear:

Sometimes it can be removed using Erase active shape, but other times it cannot.

This appears to happen most often after i have drawn a shape using Draw closed freeform, then I make a modification to the underlying image.

It is very difficult to reproduce this example as it happens sporatically and inconsistently. Attempting to filter the shapes out of canvas['layout']['shapes'] is a callback does not help.

Hi @matt.sd.watson.

Difficult to tell what’s going on but the shapes are part of the figure object. If you update your figure via a callback, the shapes should be erased.

from dash import Dash, dcc, html, Input, Output, Patch
import dash_bootstrap_components as dbc
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go
import numpy as np

# prepare trace data
data = go.Scatter(
    x=[1, 10],
    y=[1, 10],
    mode='markers',
    marker={
        'size': 8,
        'symbol': 'circle-open',
    },
)

# create figure
fig = go.Figure(data=data)

# update layout
fig.update_layout(
    template='plotly_dark',
    plot_bgcolor='rgba(0, 0, 0, 0)',
    paper_bgcolor='rgba(0, 0, 0, 0)',
    width=700,
    height=500,
    margin={
        'l': 0,
        'r': 0,
        't': 20,
        'b': 100,
    }
)

# add some shapes
for i in range(1, 6):
    fig.add_shape(
        {
            'type': 'rect',
            'x0': np.random.randint(1, 5), 'x1': np.random.randint(6, 11),
            'y0': np.random.randint(1, 5), 'y1': np.random.randint(6, 11),
        },
        editable=True,
        name=f'shape_{i}',
        line={
            'color': ['red', 'yellow', 'blue', 'pink'][np.random.randint(0, 4)],
            'width': 2,
            'dash': 'solid'
        },
    )

# Build App
app = Dash(
    __name__,
    external_stylesheets=[dbc.themes.SLATE],
    meta_tags=[
        {
            'name': 'viewport',
            'content': 'width=device-width, initial-scale=1.0'
        }
    ]
)

# app layout
app.layout = dbc.Container(
    [
        dbc.Row(
            dbc.Col(
                dcc.Graph(
                    id='graph',
                    figure=fig,
                    config={
                        'scrollZoom': True,
                        'displayModeBar': False,
                    }
                ),
                width={'size': 5, 'offset': 0}
            ), justify='around'
        ),
        dbc.Row(
            [
                dbc.Col(html.H5('Click button to delete shapes')),
            ]
        ),
        dbc.Row(
            [
                dbc.Col(
                    html.Button(
                        'Delete',
                        id='delete'
                    ),
                )
            ], justify='around'
        )
    ], fluid=True
)


@app.callback(
    Output('graph', 'figure'),
    Input('delete', 'n_clicks'),
)
def get_click(click):
    if not click:
        raise PreventUpdate
    else:
        # delete shapes
        patched = Patch()
        patched['layout']['shapes'] = []
    return patched


if __name__ == '__main__':
    app.run(debug=True, port=8053)

Even with this suggestion the shapes do not update properly. For example, on update this appears:

Even though in a callback I can print out which shapes are in the layout:

[{'editable': True, 'fillcolor': 'rgba(0, 0, 0, 0)', 'fillrule': 'evenodd', 'label': {'text': '', 'texttemplate': ''}, 'layer': 'above', 'line': {'color': 'white', 'dash': 'solid', 'width': 4}, 'opacity': 1, 'type': 'rect', 'x0': 302.01024590163945, 'x1': 462.6659836065575, 'xref': 'x', 'y0': 85.29234972677595, 'y1': 177.09562841530052, 'yref': 'y'}]

It can be seen that the only shape in the layout call is the white rectangle that I drew. The graph seems to be inserting an extra phantom shape somewhere that I cannot track in the layout.