greensock/GSAP · warning
GSDevTools can't work with ScrollTrigger-based animations; e
Error message
GSDevTools can't work with ScrollTrigger-based animations; either the scrollbar -OR- the GSDevTools scrubber can control the animation.
What it means
GSDevTools' scrubber and ScrollTrigger's scroll-driven scrubbing both want to control the animation's progress, so using both on the same animation is unsupported. When the animation assigned to GSDevTools has a scrollTrigger attached, GSDevTools warns that only one controller can own playback.
Source
Thrown at src/GSDevTools.js:759
for (; i < options.length; i++) {
list.removeChild(options[i]);
}
},
animation = function(anim) {
let ts = parseFloat(timeScale.options[timeScale.selectedIndex].value) || 1,
tl, maxDuration;
if (!arguments.length) {
return selectedAnimation;
}
if (_isString(anim)) {
anim = _getAnimationById(anim);
}
//console.log("animation() ", anim.vars.id);
if (!(anim instanceof Animation)) {
console.warn("GSDevTools error: invalid animation.");
}
if (anim.scrollTrigger) {
console.warn("GSDevTools can't work with ScrollTrigger-based animations; either the scrollbar -OR- the GSDevTools scrubber can control the animation.");
}
if (anim === selectedAnimation) {
return;
}
if (selectedAnimation) {
selectedAnimation._inProgress = inProgress;
selectedAnimation._outProgress = outProgress;
}
selectedAnimation = anim;
if (linkedAnimation) {
ts = linkedAnimation.timeScale();
if (linkedAnimation._targets && linkedAnimation._targets[0] === declaredAnimation) {
declaredAnimation.resume();
linkedAnimation.kill();
}
}
inProgress = selectedAnimation._inProgress || 0;
outProgress = selectedAnimation._outProgress || 100;View on GitHub (pinned to 13e2b79054)
Solutions
- Detach ScrollTrigger from the animation before handing it to GSDevTools
- Create a separate non-scroll-linked copy of the animation for GSDevTools debugging
- Use ScrollTrigger's own onEnter/onUpdate callbacks plus GSDevTools on an unlinked timeline
Example fix
// before
gsap.registerPlugin(GSDevTools, ScrollTrigger);
const tl = gsap.timeline({ scrollTrigger: { trigger: '.section', scrub: true } });
GSDevTools.create({ animation: tl }); // warning
// after
const tl = gsap.timeline(); // no scrollTrigger
GSDevTools.create({ animation: tl }); Defensive patterns
Strategy: validation
Validate before calling
if (animation.scrollTrigger) { console.error('Remove ScrollTrigger before attaching GSDevTools'); } else { GSDevTools.create({ animation }); } Type guard
const canUseDevTools = (anim) => anim && !anim.scrollTrigger;
Prevention
- Never attach GSDevTools to scrubbed ScrollTrigger timelines
- Debug ScrollTrigger animations by temporarily removing the scrollTrigger config
- Keep a separate unlinked copy of timelines used with GSDevTools
When it happens
Trigger: Passing an animation with anim.scrollTrigger set to GSDevTools via vars.animation, or via id lookup (getByAnimation), while ScrollTrigger scrub is attached to that same tween/timeline.
Common situations: Adding GSDevTools to an existing ScrollTrigger-scrubbed timeline for debugging; creating a timeline, attaching ScrollTrigger, then wiring it into GSDevTools; confetti of scroll-scrub demos that later get dev tools attached.
Related errors
- Please gsap.registerPlugin(ScrollTrigger)
- Requires GSAP 3.11.0 or later
- GSDevTools error: invalid animation.
- Please gsap.registerPlugin(CSSPlugin, CSSRulePlugin)
- Please gsap.registerPlugin(CustomEase, CustomBounce)
AI-assisted analysis of greensock/GSAP@13e2b79054 (2026-08-29).
Data as JSON: /api/errors/3365c42529a37f3f.
Report an issue: GitHub.