What happens
SceneDataTransformer.transform() always returns an array for annotations, even when the source had none:
// packages/scenes/src/querying/SceneDataTransformer.ts:348
return { ...data, series, annotations };
annotations is initialised to [] and only receives frames tagged
meta.dataTopic === DataTopic.Annotations, so a panel with no annotations goes from
annotations: undefined (what SceneQueryRunner emits _combineDataLayers only assigns when
_layerAnnotations.length > 0) to annotations: [].
Why it matters
Consumers guard on presence, not length. In Grafana:
TimeSeriesPanel.tsx: {data.annotations && <ExemplarsPlugin … />}
CandlestickPanel.tsx: same guard
Both now mount ExemplarsPlugin on every panel that has any transformation, with nothing to draw.
ExemplarsPlugin renders EventsCanvas, which registers a uPlot draw hook that bumps React
state on every draw — so pan, zoom and cursor redraws each cost an extra render pass per panel.
This affects any panel with user transformations today.
Suggested fix
annotations: annotations.length > 0 || data.annotations !== undefined ? annotations : undefined,
The data.annotations !== undefined clause is needed: the shorter
annotations.length ? annotations : data.annotations resurrects annotations that an
annotations-topic transformation deliberately removed. This form keeps "transformed to nothing" as
[] and returns undefined only for "never had any".
transform() is the only promotion site: SceneQueryRunner._combineDataLayers already guards.
What happens
SceneDataTransformer.transform()always returns an array forannotations, even when the source had none:annotationsis initialised to[]and only receives frames taggedmeta.dataTopic === DataTopic.Annotations, so a panel with no annotations goes fromannotations: undefined(whatSceneQueryRunneremits_combineDataLayersonly assigns when_layerAnnotations.length > 0) toannotations: [].Why it matters
Consumers guard on presence, not length. In Grafana:
TimeSeriesPanel.tsx:{data.annotations && <ExemplarsPlugin … />}CandlestickPanel.tsx: same guardBoth now mount
ExemplarsPluginon every panel that has any transformation, with nothing to draw.ExemplarsPluginrendersEventsCanvas, which registers a uPlotdrawhook that bumps Reactstate on every draw — so pan, zoom and cursor redraws each cost an extra render pass per panel.
This affects any panel with user transformations today.
Suggested fix
The
data.annotations !== undefinedclause is needed: the shorterannotations.length ? annotations : data.annotationsresurrects annotations that anannotations-topic transformation deliberately removed. This form keeps "transformed to nothing" as
[]and returnsundefinedonly for "never had any".transform()is the only promotion site:SceneQueryRunner._combineDataLayersalready guards.