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

  1. Break the cycle: keep matches on only one side of the pair (remove matches from the axis that gets the warning).
  2. Define a chain direction (x2 matches x, x3 matches x2) rather than mutual references.
  3. 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

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


AI-assisted analysis of plotly/plotly.js@1d090e0b5f (2026-09-02). Data as JSON: /api/errors/c1e960c5399a9a3d. Report an issue: GitHub.