apache/echarts · error · Error

Circular dependency may exists:

Error message

Circular dependency may exists: 

What it means

Thrown by `enableTopologicalTravel`'s `topologicalTravel` in component.ts:157. ECharts schedules component models in dependency order using Kahn's algorithm (activity-on-vertices) over the `dependencies` arrays declared on each Model (e.g. `static dependencies = ['grid', 'polar', ...]`). After draining the zero-entry queue, any main type still left in `targetNameSet` was never processed, which means it sits inside a dependency cycle. The throw is NOT guarded by `__DEV__`; only the human-readable message is — in a production build the thrown Error has an empty string as its message, so it surfaces as an Error with no text.

Source

Thrown at src/util/component.ts:157

            const currComponentType = noEntryList.pop();
            const currVertex = graph[currComponentType];
            const isInTargetNameSet = !!targetNameSet[currComponentType];
            if (isInTargetNameSet) {
                callback.call(context, currComponentType, currVertex.originalDeps.slice());
                delete targetNameSet[currComponentType];
            }
            zrUtil.each(
                currVertex.successor,
                isInTargetNameSet ? removeEdgeAndAdd : removeEdge
            );
        }

        zrUtil.each(targetNameSet, function () {
            let errMsg = '';
            if (__DEV__) {
                errMsg = makePrintable('Circular dependency may exists: ', targetNameSet, targetNameList, fullNameList);
            }
            throw new Error(errMsg);
        });

        function removeEdge(succComponentType: ComponentMainType): void {
            graph[succComponentType].entryCount--;
            if (graph[succComponentType].entryCount === 0) {
                noEntryList.push(succComponentType);
            }
        }

        // Consider this case: legend depends on series, and we call
        // chart.setOption({series: [...]}), where only series is in option.
        // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
        // not be called, but only sereis.mergeOption is called. Thus legend
        // have no chance to update its local record about series (like which
        // name of series is available in legend).
        function removeEdgeAndAdd(succComponentType: ComponentMainType): void {
            targetNameSet[succComponentType] = true;
            removeEdge(succComponentType);

View on GitHub (pinned to 30076aedcd)

Solutions

  1. Inspect the `static dependencies` arrays of any custom or third-party component models and remove the edge that closes the cycle.
  2. Update, fix, or uninstall the offending extension whose dependency metadata is cyclic.
  3. If no custom components are involved, capture the full option + ECharts version and report it as an ECharts bug.
  4. Bisect ECharts versions to identify where the cyclic dependency was introduced, then pin to the last good version while it is fixed upstream.
Defensive patterns

Strategy: try-catch

Try / catch

// topologicalTravel runs inside setOption; wrap it so a cyclic dependency
// in a third-party component degrades gracefully instead of crashing render.
try {
  chart.setOption(option);
} catch (e) {
  // In production builds the message is an empty string for this error.
  if (e instanceof Error && /Circular dependency/i.test(e.message) || (e instanceof Error && e.message === '')) {
    console.error('Component dependency cycle detected; option not applied.', option);
    // fall back to a known-good baseline option
    chart.setOption(baselineOption);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: A component main type's declared `dependencies` form a cycle (A depends on B and B depends on A, directly or transitively), reached during `GlobalModel` option merge / component scheduling (`topologicalTravel` at Global.ts:357 / :892). Built-in components are acyclic by design, so this requires a custom component model with cyclic `dependencies`, a buggy third-party extension, or an internal ECharts regression in dependency metadata.

Common situations: Authoring a custom component model whose `static dependencies` list loops back on itself or on another component that depends on it; installing a third-party ECharts plugin with incorrect dependency declarations; an ECharts version regression that introduced a cyclic dependency between built-ins; monkey-patching a built-in `dependencies` array.


AI-assisted analysis of apache/echarts@30076aedcd (2026-08-12). Data as JSON: /api/errors/a60e0b9a7295ebcd. Report an issue: GitHub.