Unity-Technologies/UnityCsReference · error · InvalidOperationException

bindings and keyframes must be of equal length

Error message

bindings and keyframes must be of equal length

What it means

Thrown by AnimationUtility.SetObjectReferenceCurves when bindings.Length != keyframes.Length. The batch API zips the two arrays element-wise; a length mismatch would either leave bindings un-synced or index out of range, so it throws InvalidOperationException before the loop and the final SyncEditorCurves.

Source

Thrown at Editor/Mono/Animation/AnimationUtility.bindings.cs:249

        extern public static EditorCurveBinding[] GetCurveBindings([NotNull] AnimationClip clip);
        extern public static EditorCurveBinding[] GetObjectReferenceCurveBindings([NotNull] AnimationClip clip);

        extern public static ObjectReferenceKeyframe[] GetObjectReferenceCurve([NotNull] AnimationClip clip, EditorCurveBinding binding);

        public static void SetObjectReferenceCurve(AnimationClip clip, EditorCurveBinding binding, ObjectReferenceKeyframe[] keyframes)
        {
            Internal_SetObjectReferenceCurve(clip, binding, keyframes, true);
            Internal_InvokeOnCurveWasModified(clip, binding, keyframes != null ? CurveModifiedType.CurveModified : CurveModifiedType.CurveDeleted);
        }

        public static void SetObjectReferenceCurves(AnimationClip clip, EditorCurveBinding[] bindings, ObjectReferenceKeyframe[][] keyframes)
        {
            if (bindings == null)
                throw new ArgumentNullException(nameof(bindings), $"{nameof(bindings)} must be non-null");
            if (keyframes == null)
                throw new ArgumentNullException(nameof(keyframes), $"{nameof(keyframes)} must be non-null");
            if (bindings.Length != keyframes.Length)
                throw new InvalidOperationException($"{nameof(bindings)} and {nameof(keyframes)} must be of equal length");

            int length = bindings.Length;
            for (int i = 0; i < length; i++)
            {
                SetObjectReferenceCurveNoSync(clip, bindings[i], keyframes[i]);
            }
            SyncEditorCurves(clip);
            Internal_InvokeOnCurveWasModified(clip, new EditorCurveBinding(), CurveModifiedType.ClipModified);
        }

        internal static void SetObjectReferenceCurveNoSync(AnimationClip clip, EditorCurveBinding binding, ObjectReferenceKeyframe[] keyframes)
        {
            Internal_SetObjectReferenceCurve(clip, binding, keyframes, false);
            Internal_InvokeOnCurveWasModified(clip, binding, keyframes != null ? CurveModifiedType.CurveModified : CurveModifiedType.CurveDeleted);
        }

        [NativeMethod(ThrowsException = true)]
        extern private static void Internal_SetObjectReferenceCurve([NotNull] AnimationClip clip, EditorCurveBinding binding, ObjectReferenceKeyframe[] keyframes, bool updateMuscleClip);

View on GitHub (pinned to 225b0fbdb5)

Solutions

  1. Assert Debug.Assert(bindings.Length == keyframes.Length, context) at construction.
  2. Build bindings and keyframes together in one pass so they cannot diverge.
  3. Add a length-check helper and call it before every Set*Curves invocation.
  4. If lengths legitimately differ, zip to the shorter length explicitly and document why.

Example fix

// before
AnimationUtility.SetObjectReferenceCurves(clip, bindings, keyframes); // mismatched lengths

// after
if (bindings.Length != keyframes.Length)
    throw new InvalidOperationException($"bindings({bindings.Length}) != keyframes({keyframes.Length}) for {clip.name}");
AnimationUtility.SetObjectReferenceCurves(clip, bindings, keyframes);
Defensive patterns

Strategy: validation

Validate before calling

if (bindings.Length != keyframes.Length) throw new InvalidOperationException($"bindings({bindings.Length}) != keyframes({keyframes.Length})");

Prevention

When it happens

Trigger: Bindings built from one set of properties and keyframes from another. A jagged keyframes array built by pushing one entry per clip but bindings built per-property. Removing an element from one array and forgetting the other.

Common situations: Migrating clips between rigs where property bindings change count. Timeline/Animator tooling that filters keyframes after building bindings. Code that appends to one list in a loop but not the other.

Related errors


AI-assisted analysis of Unity-Technologies/UnityCsReference@225b0fbdb5 (2026-08-13). Data as JSON: /api/errors/e28e54e3393fae80. Report an issue: GitHub.