plotly/plotly.js · warning
ignored <axOut._name>.scaleanchor: "<axIn.scaleanchor>" to a
Error message
ignored <axOut._name>.scaleanchor: "<axIn.scaleanchor>" to avoid either an infinite loop and possibly inconsistent scaleratios, or because this axis declares a *matches* constraint.
What it means
A scaleanchor on axOut is ignored when honoring it would either create a constraint cycle/inconsistent scaleratios or because the same axis already declares a matches constraint (matches and scaleanchor cannot coexist). The axis keeps its independent range and this warning states the reason. RAISED IN: handleOneAxDefaults during layout defaults.
Source
Thrown at src/plots/cartesian/constraints.js:297
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 ' +
'declares a *matches* constraint.');
}
}
function extent(layoutOut, ax) {
var domain = ax.domain;
if(!domain) {
// at this point overlaying axes haven't yet inherited their main domains
// TODO: constrain: domain with overlaying axes is likely a bug.
domain = layoutOut[id2name(ax.overlaying)].domain;
}
return domain[1] - domain[0];
}
function getConstraintGroup(groups, thisID) {
for(var i = 0; i < groups.length; i++) {View on GitHub (pinned to 1d090e0b5f)
Solutions
- Declare scaleanchor on only one axis of each linked pair (remove it from the axis receiving the warning).
- Choose either matches or scaleanchor for a given axis — remove the one that is not needed.
- Flatten scaleanchor chains so every axis anchors to one base axis with no cycles.
Example fix
// before
layout.xaxis2 = {matches: 'x', scaleanchor: 'x'};
// after
layout.xaxis2 = {matches: 'x'}; // or {scaleanchor: 'x', scaleratio: 1}, not both Defensive patterns
Strategy: validation
Validate before calling
function checkScaleanchorConflicts(layout) {
for (const ax of Object.keys(layout).filter(k => k.endsWith('axis'))) {
const cfg = layout[ax];
if (cfg.scaleanchor && cfg.matches) throw new Error(`${ax}: cannot combine scaleanchor and matches`);
if (cfg.scaleanchor && (layout[cfg.scaleanchor] || {}).scaleanchor === ax) throw new Error(`${ax}: mutual scaleanchor with ${cfg.scaleanchor}`);
}
}
checkScaleanchorConflicts(layout); Type guard
const hasValidScaleanchor = (layout, ax) => { const sa = (layout[ax] || {}).scaleanchor; return !sa || (!(layout[ax] || {}).matches && (layout[sa] || {}).scaleanchor !== ax); }; Prevention
- Anchor each pair from one side only.
- Never combine matches and scaleanchor on the same axis.
- Keep scaleanchor chains star-shaped (all anchor to one base).
- Comment which axis owns each anchor in shared layout code.
When it happens
Trigger: Setting layout.xaxis2.scaleanchor = 'x' where x is already scaleanchored back to x2 (cycle), or where xaxis2 also has matches set (conflict), or chains of scaleanchors that would loop.
Common situations: Duplicating scaleanchor on both axes of a pair; combining matches and scaleanchor on the same axis expecting both effects; multi-panel figures where several scaleanchors were added incrementally forming a loop.
Related errors
- ignored <axOut._name>.matches: "<axIn.matches>" to avoid an
- 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/ef94333edf3fb0de.
Report an issue: GitHub.