Unity-Technologies/UnityCsReference · error · ArgumentNullException

Not a valid handle, has it been released already?

Error message

Not a valid handle, has it been released already?

What it means

Thrown as ArgumentNullException (mis-typed exception; semantically an invalid-handle error) by ChangeTrackerHandle.PollForChanges when m_Handle == IntPtr.Zero. The handle is acquired via Internal_AcquireTracker and released via Internal_ReleaseTracker; after release the field is zeroed and any further polling is invalid. The message asks whether ReleaseTracker already ran.

Source

Thrown at Editor/Mono/ChangeTrackerHandle.bindings.cs:94

        internal extern bool HasModifiedTrackedPropertiesInternal();

        [NativeName("GetModifiedTrackedProperties")]
        private extern SerializedProperty[] GetModifiedTrackedPropertiesInternal();

        [NativeName("ClearModifiedTrackedProperties")]
        private extern void ClearModifiedTrackedPropertiesInternal();

        [FreeFunction("SerializedObjectChangeTracker::AcquireTracker")]
        private static extern IntPtr Internal_AcquireTracker(UnityEditor.SerializedObject o);

        [FreeFunction("SerializedObjectChangeTracker::ReleaseTracker", IsThreadSafe = true)]
        private static extern void Internal_ReleaseTracker(IntPtr handle);

        // returns true if object changed since last poll
        internal bool PollForChanges(bool updateSerializedObject)
        {
            if (m_Handle == IntPtr.Zero)
                throw new ArgumentNullException("Not a valid handle, has it been released already?");
            bool result = Internal_PollChanges(updateSerializedObject);

            if (HasModifiedTrackedPropertiesInternal())
            {
                m_ModifiedTrackedProperties = GetModifiedTrackedPropertiesInternal();
                for (int i = 0; i < m_ModifiedTrackedProperties.Length; ++i)
                {
                    m_ModifiedTrackedProperties[i].m_SerializedObject = m_SerializedObject;
                }

                ClearModifiedTrackedPropertiesInternal();
            }
            else
            {
                m_ModifiedTrackedProperties = s_EmptyPropertyArray;
            }

            return result;

View on GitHub (pinned to 225b0fbdb5)

Solutions

  1. Stop calling PollForChanges after ReleaseTracker; null/track the released state on the managed side.
  2. Wrap tracker usage in a using/IDisposable pattern so release and last-use cannot be reordered.
  3. After release, set the managed field holding the tracker to null and null-check before polling.

Example fix

// before
tracker.Release();
...
tracker.PollForChanges(true);

// after
tracker.Release();
tracker = null;
...
if (tracker != null) tracker.PollForChanges(true);
Defensive patterns

Strategy: validation

Validate before calling

if (tracker == null || tracker.handle == IntPtr.Zero) return;
tracker.PollForChanges(updateSerializedObject);

Try / catch

try { tracker.PollForChanges(updateSerializedObject); }
catch (ArgumentNullException) { /* handle already released; re-acquire */ }

Prevention

When it happens

Trigger: Calling tracker.PollForChanges(updateSerializedObject) after Internal_ReleaseTracker has been called on the handle (or after the tracker was never acquired). Also possible if the tracker object is reused after being released by the SerializedObjectChangeTracker native side.

Common situations: Editor inspection tooling that caches a ChangeTrackerHandle and polls across editor events, then releases the handle but keeps polling on a stale reference. Domain reload invalidating the native handle while the managed wrapper persists. Code paths that release on one event and poll on the next.

Related errors


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