greensock/GSAP · warning
Element not found: ${t}
Error message
Element not found: ${t} What it means
Observer's _getTarget helper resolves the `target` config to a DOM element via gsap.utils.toArray. When the selector/element cannot be resolved it warns "Element not found: <t>" and returns null (which makes Observer fall back to the document element in init). This library throws it so you know your selector matched nothing rather than silently observing the wrong element.
Source
Thrown at src/Observer.js:57
if (value || value === 0) {
_startup && (_win.history.scrollRestoration = "manual"); // otherwise the new position will get overwritten by the browser onload.
let isNormalizing = _normalizer && _normalizer.isPressed;
value = cachingFunc.v = Math.round(value) || (_normalizer && _normalizer.iOS ? 1 : 0); //TODO: iOS Bug: if you allow it to go to 0, Safari can start to report super strange (wildly inaccurate) touch positions!
f(value);
cachingFunc.cacheID = _scrollers.cache;
isNormalizing && _bridge("ss", value); // set scroll (notify ScrollTrigger so it can dispatch a "scrollStart" event if necessary
} else if (doNotCache || _scrollers.cache !== cachingFunc.cacheID || _bridge("ref")) {
cachingFunc.cacheID = _scrollers.cache;
cachingFunc.v = f();
}
return cachingFunc.v + cachingFunc.offset;
};
cachingFunc.offset = 0;
return f && cachingFunc;
},
_horizontal = {s: _scrollLeft, p: "left", p2: "Left", os: "right", os2: "Right", d: "width", d2: "Width", a: "x", sc: _scrollCacheFunc(function(value) { return arguments.length ? _win.scrollTo(value, _vertical.sc()) : _win.pageXOffset || _doc[_scrollLeft] || _docEl[_scrollLeft] || _body[_scrollLeft] || 0})},
_vertical = {s: _scrollTop, p: "top", p2: "Top", os: "bottom", os2: "Bottom", d: "height", d2: "Height", a: "y", op: _horizontal, sc: _scrollCacheFunc(function(value) { return arguments.length ? _win.scrollTo(_horizontal.sc(), value) : _win.pageYOffset || _doc[_scrollTop] || _docEl[_scrollTop] || _body[_scrollTop] || 0})},
_getTarget = (t, self) => ((self && self._ctx && self._ctx.selector) || gsap.utils.toArray)(t)[0] || (typeof(t) === "string" && gsap.config().nullTargetWarn !== false ? console.warn("Element not found:", t) : null),
_isWithin = (element, list) => { // check if the element is in the list or is a descendant of an element in the list.
let i = list.length;
while (i--) {
if (list[i] === element || list[i].contains(element)) {
return true;
}
}
return false;
},
_getScrollFunc = (element, {s, sc}) => { // we store the scroller functions in an alternating sequenced Array like [element, verticalScrollFunc, horizontalScrollFunc, ...] so that we can minimize memory, maximize performance, and we also record the last position as a ".rec" property in order to revert to that after refreshing to ensure things don't shift around.
_isViewport(element) && (element = _doc.scrollingElement || _docEl);
let i = _scrollers.indexOf(element),
offset = sc === _vertical.sc ? 1 : 2;
!~i && (i = _scrollers.push(element) - 1);
_scrollers[i + offset] || _addListener(element, "scroll", _onScroll); // clear the cache when a scroll occurs
let prev = _scrollers[i + offset],
func = prev || (_scrollers[i + offset] = _scrollCacheFunc(_getProxyProp(element, s), true) || (_isViewport(element) ? sc : _scrollCacheFunc(function(value) { return arguments.length ? (element[s] = value) : element[s]; })));View on GitHub (pinned to 13e2b79054)
Solutions
- Ensure the target element exists in the DOM before creating the Observer (wrap in DOMContentLoaded or run after framework mount)
- Fix the selector string or pass an actual Element reference instead of a string
- Set gsap.config({nullTargetWarn: false}) only if a missing target is expected and falling back to the document element is acceptable
- Verify you are querying the correct document/iframe context
Example fix
// before
Observer.create({ target: '#scroller', onY: y => console.log(y) });
// after
document.addEventListener('DOMContentLoaded', () => {
const el = document.querySelector('#scroller');
if (el) Observer.create({ target: el, onY: y => console.log(y) });
}); Defensive patterns
Strategy: validation
Validate before calling
const el = typeof target === 'string' ? document.querySelector(target) : target;
if (!(el instanceof Element)) { throw new Error(`Observer target not found: ${target}`); }
new Observer({ target: el }); Type guard
function isElement(v) { return v instanceof Element; } Prevention
- Create observers after DOM ready / framework mount
- Pass Element references instead of selector strings when possible
- Centralize element lookups with a helper that asserts existence
- Temporarily keep nullTargetWarn enabled in dev to catch bad selectors
When it happens
Trigger: new Observer({target: "#missing"}) or Observer.create({target: ...}) where the selector string matches no element, the element is not yet in the DOM, or a null/undefined target is passed before the DOM is ready.
Common situations: Running before DOMContentLoaded, typos in the selector, targeting elements rendered later by a framework (React/Vue mounts), or passing a selector scoped to a different document/iframe.
Related errors
- ScrollSmoother needs a valid content element.
- scrollTo target doesn't exist. Using 0
- Element not found:
- ${elOrNode} not found
- Please gsap.registerPlugin(Observer)
AI-assisted analysis of greensock/GSAP@13e2b79054 (2026-08-29).
Data as JSON: /api/errors/b10052e85fc3e24d.
Report an issue: GitHub.