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
- Stop calling PollForChanges after ReleaseTracker; null/track the released state on the managed side.
- Wrap tracker usage in a using/IDisposable pattern so release and last-use cannot be reordered.
- 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
- Null the managed reference after Release.
- Use a using/IDisposable scope for the tracker.
- Never poll after release.
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
- ScriptableBakedReflectionSystemWrapper
- Unexpected resultBits Span length. The expected length of re
- GetInstallationForPath: Does not allow null editorPath
- GetCodeEditorForPath: Does not allow null editorPath
- SetCodeEditor: Does not allow null editorPath
AI-assisted analysis of Unity-Technologies/UnityCsReference@225b0fbdb5 (2026-08-13).
Data as JSON: /api/errors/195aa5d961141aaa.
Report an issue: GitHub.