plotly/plotly.js · warning
ignored <axOut._name>.matches: "<axIn.matches>" to avoid an
Error message
ignored <axOut._name>.matches: "<axIn.matches>" to avoid an infinite loop
What it means
When axis axOut declares matches pointing at another axis, plotly.js ignores the setting if it would create a cycle (the matched axis is part of the same group/loop) — matches is processed earlier in the chain, so honoring it would loop forever. The setting is dropped and this warning explains why. RAISED IN: handleOneAxDefaults, i.e. during layout default computation.
Source
Thrown at src/plots/cartesian/constraints.js:281
}, 'scaleanchor', scaleanchorDflt);
}
if(matches) {
axOut._matchGroup = updateConstraintGroups(matchGroups, thisID, matches, 1);
// Also include match constraints in the scale groups
var matchedAx = layoutOut[id2name(matches)];
var matchRatio = extent(layoutOut, axOut) / extent(layoutOut, matchedAx);
if(isX !== (matches.charAt(0) === 'x')) {
// We don't yet know the actual scale ratio of x/y matches constraints,
// due to possible automargins, so just leave a placeholder for this:
// 'x' means "x size over y size", 'y' means the inverse.
// in principle in the constraint group you could get multiple of these.
matchRatio = (isX ? 'x' : 'y') + matchRatio;
}
updateConstraintGroups(constraintGroups, thisID, matches, matchRatio);
} else if(axIn.matches && axIds.indexOf(axIn.matches) !== -1) {
Lib.warn('ignored ' + axOut._name + '.matches: "' +
axIn.matches + '" to avoid an infinite loop');
}
if(scaleanchor) {
var scaleratio = coerce('scaleratio');
// TODO: I suppose I could do attribute.min: Number.MIN_VALUE to avoid zero,
// but that seems hacky. Better way to say "must be a positive number"?
// Of course if you use several super-tiny values you could eventually
// force a product of these to zero and all hell would break loose...
// Likewise with super-huge values.
if(!scaleratio) scaleratio = axOut.scaleratio = 1;
updateConstraintGroups(constraintGroups, thisID, scaleanchor, scaleratio);
} else if(axIn.scaleanchor && axIds.indexOf(axIn.scaleanchor) !== -1) {
Lib.warn('ignored ' + axOut._name + '.scaleanchor: "' +
axIn.scaleanchor + '" to avoid either an infinite loop ' +
'and possibly inconsistent scaleratios, or because this axis ' +View on GitHub (pinned to 1d090e0b5f)
Solutions
- Break the cycle: keep matches on only one side of the pair (remove matches from the axis that gets the warning).
- Define a chain direction (x2 matches x, x3 matches x2) rather than mutual references.
- Restructure so all axes in the synchronized set match a single base axis.
Example fix
// before
layout.xaxis2 = {matches: 'x'};
layout.xaxis = {matches: 'x2'}; // cycle
// after
layout.xaxis2 = {matches: 'x'};
// remove matches from xaxis Defensive patterns
Strategy: validation
Validate before calling
function assertAcyclicMatches(layout) {
const seen = new Set();
const visit = (ax, chain) => {
if (chain.includes(ax)) throw new Error(`cyclic matches: ${chain.join(' -> ')} -> ${ax}`);
const m = (layout[ax] || {}).matches;
if (m) visit(m, [...chain, ax]);
};
Object.keys(layout).filter(k => k.endsWith('axis')).forEach(ax => visit(ax, []));
}
assertAcyclicMatches(layout); Type guard
const isAcyclicMatches = (layout, ax) => { const s = new Set(); let cur = (layout[ax] || {}).matches; while (cur) { if (s.has(cur)) return false; s.add(cur); cur = (layout[cur] || {}).matches; } return true; }; Prevention
- Set matches in one direction only (child matches base).
- Never set matches on both axes of a pair.
- Draw the matches graph when linking many subplots.
- Keep a single base axis for all synchronized axes.
When it happens
Trigger: Setting layout.xaxis2.matches = 'x' where x itself already matches x2 (or is linked into the same group via matches/scaleanchor chains), creating a circular matches reference among axes.
Common situations: Copy-pasting axis configs and duplicating matches on both axes; programmatically linking subplot axes in a loop (x2 matches x3, x3 matches x2); templates that apply matches broadly.
Related errors
- ignored <axOut._name>.scaleanchor: "<axIn.scaleanchor>" to a
- fixedrange was specified as false for axis <axName2> but was
- c2dApply: axArray and valArray must be the same length
- Array attribute <refAttr> has more entries than expected, tr
- Array attribute <refAttr> has fewer entries than expected, e
AI-assisted analysis of plotly/plotly.js@1d090e0b5f (2026-09-02).
Data as JSON: /api/errors/c1e960c5399a9a3d.
Report an issue: GitHub.