chartjs/Chart.js · critical · Error
Canvas is already in use. Chart with ID '${existingChart.id}
Error message
Canvas is already in use. Chart with ID '${existingChart.id}' must be destroyed before the canvas with ID '${existingChart.canvas.id}' can be reused. What it means
The Chart constructor calls getCanvas(item) then getChart(initialCanvas); the latter looks up a registry of live Chart instances keyed by canvas. If a Chart already exists on that canvas, the constructor refuses to create a second one and throws, naming the existing chart's id and canvas id. This guards against double-initialization which would leak listeners, corrupt the platform context, and double-render.
Source
Thrown at src/core/core.controller.js:128
static getChart = getChart;
static register(...items) {
registry.add(...items);
invalidatePlugins();
}
static unregister(...items) {
registry.remove(...items);
invalidatePlugins();
}
// eslint-disable-next-line max-statements
constructor(item, userConfig) {
const config = this.config = new Config(userConfig);
const initialCanvas = getCanvas(item);
const existingChart = getChart(initialCanvas);
if (existingChart) {
throw new Error(
'Canvas is already in use. Chart with ID \'' + existingChart.id + '\'' +
' must be destroyed before the canvas with ID \'' + existingChart.canvas.id + '\' can be reused.'
);
}
const options = config.createResolver(config.chartOptionScopes(), this.getContext());
this.platform = new (config.platform || _detectPlatform(initialCanvas))();
this.platform.updateConfig(config);
const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);
const canvas = context && context.canvas;
const height = canvas && canvas.height;
const width = canvas && canvas.width;
this.id = uid();
this.ctx = context;
this.canvas = canvas;View on GitHub (pinned to cb02e1d207)
Solutions
- Destroy the existing chart before recreating: keep a ref and call existingChart.destroy() in your cleanup/effect-return before `new Chart`.
- Reuse the instance: call chart.update() / chart.data = ...; chart.update() instead of new Chart() when only data changes.
- Use Chart.getChart(canvas) to fetch the live instance and update it rather than constructing a new one.
- In framework lifecycle hooks, ensure the destroy runs on unmount (e.g. useEffect(() => { const c = new Chart(...); return () => c.destroy(); }, [])).
Example fix
// before
function render() {
new Chart(canvas, config); // re-runs on every render -> throws
}
// after
let chart;
function render() {
if (chart) { chart.destroy(); chart = null; }
chart = new Chart(canvas, config);
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure the canvas is free (or reuse the existing instance) before constructing.
import { Chart } from 'chart.js';
function getOrCreateChart(canvas, config) {
const existing = Chart.getChart(canvas);
if (existing) {
// Option A: reuse and update; Option B: destroy then recreate.
existing.data = config.data;
existing.options = config.options;
existing.update();
return existing;
}
return new Chart(canvas, config);
} Prevention
- In framework effects, always return a destroy() cleanup: useEffect(() => { const c = new Chart(...); return () => c.destroy(); }, []).
- Prefer chart.update() over re-instantiating when only data changes.
- Keep the Chart instance in a ref/store so you can destroy it deterministically.
- Disable React Strict double-invoke pitfalls by idempotently destroying before creating.
When it happens
Trigger: Calling `new Chart(canvas, ...)` twice on the same canvas/2d-context/element without calling `.destroy()` on the first; HMR / React/VE component re-render that creates a Chart in an effect without a cleanup destroy; SPAs navigating away and back while the prior Chart instance was never disposed.
Common situations: React/Vue/Svelte lifecycle where the effect/fn re-runs (Strict Mode double-invoke in React 18 dev, dev hot-reload); reusing a canvas element reference held outside the chart; rebuilding a dashboard widget on data change by re-instantiating instead of calling chart.update().
AI-assisted analysis of chartjs/Chart.js@cb02e1d207 (2026-08-12).
Data as JSON: /api/errors/9e7a6653f1739ad4.
Report an issue: GitHub.