Unity-Technologies/UnityCsReference · error · ArgumentNullException

keyframes must be non-null

Error message

keyframes must be non-null

What it means

Thrown by AnimationUtility.SetObjectReferenceCurves when the keyframes array (the jagged ObjectReferenceKeyframe[][]) is null. It is checked right after bindings so that the subsequent length-comparison guard has a real Length on both sides. SetObjectReferenceCurve (singular) allows null keyframes to delete a curve, but the plural batch form requires non-null.

Solutions

  1. Default keyframes to an empty jagged array when there is nothing to write.
  2. Build keyframes to exactly mirror bindings length, element for element.
  3. Map a null result to Array.Empty<ObjectReferenceKeyframe[]>() at the boundary.
  4. If you intend to delete all curves, call SetObjectReferenceCurve per binding with null instead of the batch API.

Example fix

// before
var keyframes = clipData.HasKeyframes ? BuildKeyframes() : null;
AnimationUtility.SetObjectReferenceCurves(clip, bindings, keyframes);

// after
var keyframes = clipData.HasKeyframes ? BuildKeyframes() : Array.Empty<ObjectReferenceKeyframe[]>();
AnimationUtility.SetObjectReferenceCurves(clip, bindings, keyframes);
Defensive patterns

Strategy: validation

Validate before calling

if (keyframes == null) keyframes = Array.Empty<ObjectReferenceKeyframe[]>();

Prevention

When it happens

Trigger: Calling SetObjectReferenceCurves(clip, bindings, null). A collector that returns null for 'no keyframes' instead of an empty jagged array. Deserialized keyframe data where the outer array node was missing.

Common situations: Programmatic clip generation that conditionally produces keyframes and defaults to null. JSON/serialization round-trips where an empty keyframes list serialized as null on load. Copy-paste from the singular API where null is legal.

Related errors


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

Appendix: source

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

        extern internal static PropertyModification EditorCurveBindingToPropertyModification(EditorCurveBinding binding, [NotNull] GameObject gameObject);

        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);
        }

View on GitHub (pinned to 225b0fbdb5)