greensock/GSAP · warning
Error: GSDevTools can only have one instance that's globally
Error message
Error: GSDevTools can only have one instance that's globally synchronized.
What it means
GSDevTools synchronizes all instances through a single hidden root tween; only one instance can own that global sync at a time. Creating a second instance with vars.globalSync (true by default, or when animating the _recordedRoot) warns that the previous instance loses sync ownership.
Source
Thrown at src/GSDevTools.js:790
declaredAnimation.resume();
linkedAnimation.kill();
}
}
inProgress = selectedAnimation._inProgress || 0;
outProgress = selectedAnimation._outProgress || 100;
inPoint.style.left = inProgress + "%";
outPoint.style.left = outProgress + "%";
if (_fullyInitialized) { //don't record inProgress/outProgress unless we're fully instantiated because people may call GSDevTools.create() before creating/defining their animations, thus the inTime/outTime may not exist yet.
record("animation", selectedAnimation.vars.id);
record("in", inProgress);
record("out", outProgress);
}
startTime = 0;
maxDuration = vars.maxDuration || Math.min(1000, _getClippedDuration(selectedAnimation));
if (selectedAnimation === _recordedRoot || vars.globalSync) {
_merge();
linkedAnimation = _rootTween;
_rootInstance && _rootInstance !== _self && console.warn("Error: GSDevTools can only have one instance that's globally synchronized.");
_rootInstance = _self;
if (selectedAnimation !== _recordedRoot) {
tl = selectedAnimation;
endTime = tl.totalDuration();
if (endTime > 99999999) { //in the case of an infinitely repeating animation, just use a single iteration's duration instead.
endTime = tl.duration();
}
while (tl.parent) {
startTime = (startTime / tl._ts) + tl._start;
endTime = (endTime / tl._ts) + tl._start;
tl = tl.parent;
}
} else {
endTime = _recordedRoot.duration();
}
if (endTime - startTime > maxDuration) { //cap end time at 1000 because it doesn't seem reasonable to accommodate super long stuff.
endTime = startTime + maxDuration;
}View on GitHub (pinned to 13e2b79054)
Solutions
- Kill the previous instance before creating a new one (GSDevTools.getByAnimation(animation).kill() or instance.kill())
- Set globalSync:false on all but one instance
- Guard creation so only one instance exists app-wide (module-level singleton)
Example fix
// before
class Panel { mount() { this.devTools = GSDevTools.create({ animation: tl }); } }
// after
class Panel {
mount() {
this.devTools?.kill?.();
const existing = GSDevTools.getByAnimation(tl);
if (existing) existing.kill();
this.devTools = GSDevTools.create({ animation: tl, globalSync: false });
}
} Defensive patterns
Strategy: validation
Validate before calling
if (GSDevTools.getByAnimation(animation)) { /* instance exists */ } else { GSDevTools.create({ animation }); } Type guard
const hasActiveGlobalInstance = () => !!document.querySelector('.gs-dev-tools'); Prevention
- Kill GSDevTools instances on component unmount
- Use a module-level singleton for the tool
- Set globalSync:false for secondary instances
- Watch for React StrictMode/HMR double-mounts
When it happens
Trigger: Calling GSDevTools.create() twice with globalSync not disabled; creating instances in multiple components/pages that each default to globalSync; leaving an old instance alive when mounting a new one (e.g. in React/HMR re-renders).
Common situations: React strict-mode or HMR double-mounting a component that calls GSDevTools.create(); opening the tool on the global timeline in several widgets on one page; SPA navigation without killing the previous instance.
Related errors
- [GSDevTools warning] only one instance can be affected by ke
- GSDevTools error: invalid animation.
- Please gsap.registerPlugin(CSSPlugin, CSSRulePlugin)
- Please gsap.registerPlugin(CustomEase, CustomBounce)
- Please gsap.registerPlugin(CustomEase)
AI-assisted analysis of greensock/GSAP@13e2b79054 (2026-08-29).
Data as JSON: /api/errors/e426f572004cd4c6.
Report an issue: GitHub.