{"record":{"id":"9e7a6653f1739ad4","repo":"chartjs/Chart.js","slug":"canvas-is-already-in-use-chart-with-id-existin","errorCode":null,"errorMessage":"Canvas is already in use. Chart with ID '${existingChart.id}' must be destroyed before the canvas with ID '${existingChart.canvas.id}' can be reused.","messagePattern":"Canvas is already in use\\. Chart with ID '(.+?)' must be destroyed before the canvas with ID '(.+?)' can be reused\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/core/core.controller.js","lineNumber":128,"sourceCode":"  static getChart = getChart;\n\n  static register(...items) {\n    registry.add(...items);\n    invalidatePlugins();\n  }\n\n  static unregister(...items) {\n    registry.remove(...items);\n    invalidatePlugins();\n  }\n\n  // eslint-disable-next-line max-statements\n  constructor(item, userConfig) {\n    const config = this.config = new Config(userConfig);\n    const initialCanvas = getCanvas(item);\n    const existingChart = getChart(initialCanvas);\n    if (existingChart) {\n      throw new Error(\n        'Canvas is already in use. Chart with ID \\'' + existingChart.id + '\\'' +\n\t\t\t\t' must be destroyed before the canvas with ID \\'' + existingChart.canvas.id + '\\' can be reused.'\n      );\n    }\n\n    const options = config.createResolver(config.chartOptionScopes(), this.getContext());\n\n    this.platform = new (config.platform || _detectPlatform(initialCanvas))();\n    this.platform.updateConfig(config);\n\n    const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);\n    const canvas = context && context.canvas;\n    const height = canvas && canvas.height;\n    const width = canvas && canvas.width;\n\n    this.id = uid();\n    this.ctx = context;\n    this.canvas = canvas;","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/chartjs/Chart.js/blob/cb02e1d207bd4c4c40b20c259017f85f26f1e30a/src/core/core.controller.js#L110-L146","documentation":"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.","triggerScenarios":"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.","commonSituations":"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().","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(); }, []))."],"exampleFix":"// before\nfunction render() {\n  new Chart(canvas, config); // re-runs on every render -> throws\n}\n\n// after\nlet chart;\nfunction render() {\n  if (chart) { chart.destroy(); chart = null; }\n  chart = new Chart(canvas, config);\n}","handlingStrategy":"validation","validationCode":"// Ensure the canvas is free (or reuse the existing instance) before constructing.\nimport { Chart } from 'chart.js';\n\nfunction getOrCreateChart(canvas, config) {\n  const existing = Chart.getChart(canvas);\n  if (existing) {\n    // Option A: reuse and update; Option B: destroy then recreate.\n    existing.data = config.data;\n    existing.options = config.options;\n    existing.update();\n    return existing;\n  }\n  return new Chart(canvas, config);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["lifecycle","memory-leak","framework-integration","canvas"],"backgroundTag":null,"analyzedSha":"cb02e1d207bd4c4c40b20c259017f85f26f1e30a","analyzedAt":"2026-08-12T23:38:10.965Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}