Chart randomly does not render

I have a dashboard with several tabs and charts/tables. The tab that renders on initial page load has an array of five gauge charts (pie charts cut in half with a hole and a line drawn for an indicator) at the top. It looks like this:


Occasionally, one of the charts will not render, like this:

Here is the error I get in the console:
GaugeError
Upgrading to Dash 1.2 seems to have fixed the issue if I run the application locally, but when run from the server, the problem still persists. Sometimes it fails every page load, and sometimes there can be 30+ page loads before a chart doesn’t render, and it’s not always the same chart.

The code for the entire app is very long, but here’s where the gauges are in the layout (the link tags are so the charts can be clickable to be used as filters for a bar chart below):

html.Div(
    id='top',
    children=[
        html.Div(
            id='gauge_div',
            children=[
                html.Div(
                    id='gauge_title'
                    )] + [
                html.A(
                    id=f'link{i+1}',
                    className='gauge_link',
                    children=dcc.Graph(
                        id=f'gauge{i+1}',
                        config={
                            "displaylogo": False,
                            'modeBarButtonsToRemove': [
                                i for i in modeBar_opts if i not
                                in ['toImage']]
                        }
                    )
                ) for i in range(len(rating_titles))
            ]
        )
    ]
)

Callback:

@app.callback(
	[Output(f'gauge{i+1}', 'figure') for i in range(len(rating_titles))] +
	[Output(f'link{i+1}', 'style') for i in range(len(rating_titles))],
	[Input('dash_data', 'children'),
	 Input('selected', 'value')]
)
def update_gauge(df, selected):
	'''When gauge chart selected or filtered data updated, remake gauge
	charts with special styling for selected gauge; if no data, hide all'''
	if df and len(pd.DataFrame(json.loads(df))) > 0:
		dff = pd.DataFrame(json.loads(df))
		dff.loc[:, 'startdate'] = dff.apply(
			lambda x: datetime.fromtimestamp(x['startdate']/1000), axis=1)
		dff['startdate'] = dff['startdate'].dt.date
		gauges = []
		for i in range(1, len(rating_titles) + 1):
			if i:
				if selected == str(i) or selected == i:
					color = '#777373'
					weight = ['<b>', '</b>']
				else:
					color = 'rgba(255,0,0,0)'
					weight = ['', '']
			else:
				color = 'rgba(255,0,0,0)'
				weight = ['', '']
			gauges.append(
				gauge_chart(
					dff, rating_titles, f'{rating_array}[{i}]', color=color,
					weight=weight))
		return gauges + [
			{'visibility': 'visible'} for _ in range(len(rating_titles))]
	else:
		fig = {'data': go.Bar(), 'layout': go.Layout()}
		return [fig for _ in range(len(rating_titles))] + [
			{'visibility': 'hidden'} for _ in range(len(rating_titles))]

Here’s the gauge_chart() function that’s called in the callback:

def gauge_chart(df, rating_titles, col, color='rgba(255,0,0,0)',
				weight=['', '']):
	if weight == ['', '']:
		opac = 0.6
		indic = '#00000099'
	else:
		opac = 1
		indic = 'black'
	RatingOutOf = 5
	divisions = RatingOutOf - 1
	AverageRating = df[col].mean()
	Angle = 180 - (((AverageRating - 1) / (RatingOutOf - 1)) * 180)
	colors = ['rgba(255,0,0,0)'] + [
		c.get_hex() for c in Color('green').range_to('red', divisions)
		]
	vals = [50] + [(50/divisions)]*divisions
	catNum = int(col.split('[')[1][:-1])
	Title = '<br>'.join(textwrap.wrap(
		rating_titles[catNum - 1], width=30))
	Origin = (0.5, 0.5)
	Length = .4
	EndX = Origin[0] + (math.cos(math.radians(Angle)) * Length)
	EndY = Origin[1] + math.sqrt(abs(Length**2 - (abs(EndX - Origin[0]))**2))
	gauge = {
		'values': vals,
		'opacity': opac,
		'textposition': 'none',
		'rotation': 90,
		'sort': False,
		'domain': {'x': [0, 1], 'y': [-1, 1]},
		'xref': 'paper',
		'yref': 'paper',
		'hole': .6,
		'type': 'pie',
		'marker': {
			'colors': colors,
			'line': {'color': 'rgba(255,0,0,0)', 'width': 0}
		},
		'showlegend': False,
		'hoverinfo': 'none'
	}
	layout = {
		'title': f'{weight[0]}{Title}{weight[1]}',
		'width': 350,
		'height': 350,
		'modebar': {
			'bgcolor': 'rgba(0,0,0,0)',
			'color': '#2262AF',
			'activecolor': '#85AD40'
		},
		'plot_bgcolor': 'rgba(255,0,0,0)',
		'paper_bgcolor': 'rgba(255,0,0,0)',
		'margin': go.layout.Margin(
			l=10,
			r=10,
			b=10,
			t=50
		),
		'annotations': [
			{
				'font': {'size': 24},
				'showarrow': False,
				'text': f'{weight[0]}{round(AverageRating, 1)}{weight[1]}',
				'x': Origin[0],
				'y': Origin[1] + 0.02,
				'xref': 'paper',
				'yref': 'paper'
			},
			{
				'font': {'size': 12},
				'showarrow': False,
				'text': '',
				'x': Origin[0],
				'y': Origin[1],
				'xref': 'paper',
				'yref': 'paper'
			}
		],
		'shapes': [
			{
				'type': 'rect',
				'fillcolor': 'white',
				'x0': 0,
				'y0': Origin[1] - 0.15,
				'x1': 1,
				'y1': 1.2,
				'line': {'width': 0, 'color': 'rgba(255,0,0,0)'},
				'xref': 'paper',
				'yref': 'paper',
				'layer': 'below'
			},
			{
				'type': 'line',
				'x0': Origin[0],
				'y0': Origin[1],
				'x1': EndX,
				'y1': EndY,
				'line': {'width': 7, 'color': indic},
				'xref': 'paper',
				'yref': 'paper'
			},
			{
				'type': 'circle',
				'fillcolor': 'white',
				'x0': Origin[0] - 0.15,
				'y0': Origin[1] - 0.15,
				'x1': Origin[0] + 0.15,
				'y1': Origin[1] + 0.15,
				'line': {'width': 0},
				'xref': 'paper',
				'yref': 'paper'
			}
		]
	}
	fig = {'data': [gauge], 'layout': layout}
	return fig

Thanks for making this bug report over at plotly.js! You may be interested though to know that the latest plotly.js version (1.49) introduces an indicator trace type that should save you some headaches https://plot.ly/javascript/indicator/