greensock/GSAP · warning

Not a valid state object.

Error message

Not a valid state object.

What it means

Flip's _fromTo (backing Flip.from/Flip.to) requires both fromState and toState to be FlipState instances. If either argument is not a valid state object (e.g. undefined, a plain object, or the result of a failed getState), it warns "Not a valid state object." and proceeds only after the check, risking downstream errors.

Source

Thrown at src/Flip.js:323

				_bodyLocked= lock;
			} else if (_bodyLocked) {
				while (i--) {
					_bodyMetrics[i] ? (s[_bodyProps[i]] = _bodyMetrics[i]) : s.removeProperty(_camelToDashed(_bodyProps[i]));
				}
				_bodyLocked = lock;
			}
		}
	},
	_revertTempStyles = (temps, stateIndex) => { // in _fromTo(), we store the inline styles temporarily when nested is true, and the Array is like [element, styles1, styles2, element, styles1, styles2, ...] where styles1 is one state and styles2 is another state (the element.getAttribute("style")).
		for (let i = 0; i < temps.length; i+=3) {
			gsap.set(temps[i], {clearProps: true}); // to clear cached transforms too
			temps[i].setAttribute("style", temps[i+stateIndex]);
			temps[i]._gsap.gmCache = -1; // bust the globalMatrix cache
		}
	},

	_fromTo = (fromState, toState, vars, relative) => { // relative is -1 if "from()", and 1 if "to()"
		(fromState instanceof FlipState && toState instanceof FlipState) || console.warn("Not a valid state object.");
		vars = vars || {};
		let { clearProps, onEnter, onLeave, absolute, absoluteOnLeave, custom, delay, paused, repeat, repeatDelay, yoyo, toggleClass, nested, zIndex, scale, fade, stagger, spin, prune } = vars,
			props = ("props" in vars ? vars : fromState).props,
			tweenVars = _copy(vars, _reserved),
			animation = gsap.timeline({ delay, paused, repeat, repeatDelay, yoyo, data: "isFlip" }),
			remainingProps = tweenVars,
			entering = [],
			leaving = [],
			comps = [],
			swapOutTargets = [],
			spinNum = spin === true ? 1 : spin || 0,
			spinFunc = typeof(spin) === "function" ? spin : () => spinNum,
			interrupted = fromState.interrupted || toState.interrupted,
			addFunc = animation[relative !== 1 ? "to" : "from"],
			v, p, endTime, i, el, comp, state, targets, finalStates, fromNode, toNode, run, a, b;
		//relative || (toState = (new FlipState(toState.targets, {props: props})).fit(toState, scale));
		for (p in toState.idLookup) {
			toNode = !toState.alt[p] ? toState.idLookup[p] : _getChangingElState(toState, fromState, p);

View on GitHub (pinned to 13e2b79054)

Solutions

  1. Always pass the return value of Flip.getState() directly
  2. Never serialize/deserialize FlipState through JSON — re-capture state instead
  3. Ensure one copy of Flip/gsap in the bundle
  4. Check Flip.from(state) receives a truthy state (log it if unsure)

Example fix

// before
const saved = JSON.parse(localStorage.getItem('flipState'));
Flip.from(saved); // plain object, not FlipState
// after
// re-capture state in the live DOM instead of persisting class instances
const state = Flip.getState('.card');
Flip.from(state);
Defensive patterns

Strategy: validation

Validate before calling

if (!(fromState instanceof FlipState) || !(toState instanceof FlipState)) {
  throw new Error('Flip.from/to require FlipState objects from Flip.getState()');
}

Type guard

function isFlipState(s) {
  return s instanceof FlipState || (s && s.idLookup && s.elementStates && Array.isArray(s.elementStates));
}

Try / catch

try {
  Flip.from(state);
} catch (err) {
  console.error('Flip state invalid — re-capture with Flip.getState()', err);
  state = Flip.getState('.target');
  Flip.from(state);
}

Prevention

When it happens

Trigger: Passing a plain object instead of Flip.getState() output to Flip.from()/Flip.to(); a Flip.getState() call that failed/was overwritten; JSON-serialized state (loses class identity so instanceof FlipState fails); passing state from a different Flip copy.

Common situations: Persisting state to localStorage/JSON and restoring it; two bundled copies of Flip so instanceof checks fail; typo like Flip.from(state.vars); calling Flip.from() with no argument.

Related errors


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