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

  1. Detach ScrollTrigger from the animation before handing it to GSDevTools
  2. Create a separate non-scroll-linked copy of the animation for GSDevTools debugging
  3. 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

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


AI-assisted analysis of greensock/GSAP@13e2b79054 (2026-08-29). Data as JSON: /api/errors/3365c42529a37f3f. Report an issue: GitHub.