{"record":{"id":"323cffc12a8bcfdf","repo":"chartjs/Chart.js","slug":"cannot-determine-type-of-id-axis-please-prov","errorCode":null,"errorMessage":"Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.","messagePattern":"Cannot determine type of '(.+?)' axis\\. Please provide 'axis' or 'position' option\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/core/core.config.js","lineNumber":52,"sourceCode":"  }\n  if (position === 'left' || position === 'right') {\n    return 'y';\n  }\n}\n\nexport function determineAxis(id, ...scaleOptions) {\n  if (idMatchesAxis(id)) {\n    return id;\n  }\n  for (const opts of scaleOptions) {\n    const axis = opts.axis\n      || axisFromPosition(opts.position)\n      || id.length > 1 && idMatchesAxis(id[0].toLowerCase());\n    if (axis) {\n      return axis;\n    }\n  }\n  throw new Error(`Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.`);\n}\n\nfunction getAxisFromDataset(id, axis, dataset) {\n  if (dataset[axis + 'AxisID'] === id) {\n    return {axis};\n  }\n}\n\nfunction retrieveAxisFromDatasets(id, config) {\n  if (config.data && config.data.datasets) {\n    const boundDs = config.data.datasets.filter((d) => d.xAxisID === id || d.yAxisID === id);\n    if (boundDs.length) {\n      return getAxisFromDataset(id, 'x', boundDs[0]) || getAxisFromDataset(id, 'y', boundDs[0]);\n    }\n  }\n  return {};\n}\n","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/chartjs/Chart.js/blob/cb02e1d207bd4c4c40b20c259017f85f26f1e30a/src/core/core.config.js#L34-L70","documentation":"determineAxis() tries to infer the axis ('x', 'y', or 'r') for a scale id by checking, in order: whether the id itself is 'x'/'y'/'r', the scale config's `axis` property, the `position` property (top/bottom -> x, left/right -> y), and finally a single-letter prefix of the id. If none of these yield an axis, Chart.js cannot know how to place or orient the scale and throws this error during config merging (mergeScaleConfig), before the chart renders.","triggerScenarios":"Declaring a scale whose id is not 'x'/'y'/'r' (e.g. a multi-char id like 'myScale' or 'temperature') and omitting both the `axis` and `position` options on that scale config; or passing a scale config object that is missing both keys. Thrown at config build time inside Config/mergeScaleConfig -> determineAxis.","commonSituations":"Renaming scale ids for readability (e.g. 'yTemp') without adding position; copying a multi-axis config snippet that used custom ids and dropping the position field; v3->v4 migration where previously inferred axes now require explicit `position`; SSR or unit-test setups feeding minimal scale objects.","solutions":["Add an explicit `position` to the scale: scales: { myScale: { type: 'linear', position: 'left' } }.","Or set the `axis` property directly: scales: { myScale: { type: 'linear', axis: 'y' } }.","Or name the scale id with a single leading axis letter it should map to (id 'y' / 'x' / 'r' match directly; a 2+ char id whose first char lowercased is x/y/r is accepted as a fallback).","Audit every custom scale id in options.scales and confirm each has either position or axis set."],"exampleFix":"// before\nscales: {\n  temp: { type: 'linear' }\n}\n\n// after\nscales: {\n  temp: { type: 'linear', position: 'left' }\n}","handlingStrategy":"validation","validationCode":"// Validate every scale config has a determinable axis before constructing the chart.\nfunction validateScales(scales) {\n  const known = new Set(['x', 'y', 'r']);\n  const posToAxis = { top: 'x', bottom: 'x', left: 'y', right: 'y' };\n  for (const [id, cfg] of Object.entries(scales ?? {})) {\n    const c = cfg || {};\n    const axis = known.has(id) ? id\n      : c.axis\n      || posToAxis[c.position]\n      || (id.length > 1 && known.has(id[0].toLowerCase()) ? id[0].toLowerCase() : null);\n    if (!axis) {\n      throw new Error(`Scale '${id}' needs 'axis' or 'position' (top|bottom|left|right).`);\n    }\n  }\n}\nvalidateScales(config.options?.scales);","typeGuard":"// Narrow scale options to a shape that always carries axis info.\ntype PositionedScale = { type?: string; axis?: 'x' | 'y' | 'r'; position?: 'top' | 'bottom' | 'left' | 'right' };\nfunction isPositionedScale(s: unknown): s is PositionedScale {\n  if (!s || typeof s !== 'object') return false;\n  const o = s as PositionedScale;\n  return Boolean(o.axis || ['top', 'bottom', 'left', 'right'].includes(o.position as string));\n}","tryCatchPattern":null,"preventionTips":["Adopt a lint rule or wrapper that rejects scale configs lacking position/axis for custom ids.","Prefer standard ids 'x'/'y'/'r' for primary axes so inference works without extra config.","When refactoring scale ids, run a quick render test to catch missing axis inference.","Keep a config schema (JSON Schema / zod) for chart options in shared libraries."],"tags":["scales","configuration","axis","validation"],"backgroundTag":null,"analyzedSha":"cb02e1d207bd4c4c40b20c259017f85f26f1e30a","analyzedAt":"2026-08-12T23:38:10.965Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}