Description
GripperBase.SetColliderSize throws a NullReferenceException when Pick() is invoked during the OnEnable phase — before GripperBase.Start() has cached the BoxCollider reference.
This happens whenever a Cylinder end-position event (OnLimitMinEvent / OnLimitMaxEvent) is wired in the Inspector to GripperBase.Pick() and the gripper has Dynamic Size enabled. Property.Subscribe fires the handler immediately with the current value, so the whole pick chain executes inside Cylinder.OnEnable().
Stack trace
NullReferenceException: Object reference not set to an instance of an object
OC.MaterialFlow.GripperBase.SetColliderSize (System.Boolean isGripped) (at Runtime/Scripts/MaterialFlow/GripperBase.cs:185)
OC.MaterialFlow.GripperBase.OnIsActiveChanged (System.Boolean value) (at Runtime/Scripts/MaterialFlow/GripperBase.cs:148)
OC.Property`1[T].set_Value (T value) (at Runtime/Scripts/Internal/Property.cs:42)
OC.MaterialFlow.GripperBase.Pick () (at Runtime/Scripts/MaterialFlow/GripperBase.cs:96)
UnityEngine.Events.InvokableCall.Invoke ()
UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0)
OC.Property`1[T].Subscribe (System.Action`1[T] action) (at Runtime/Scripts/Internal/Property.cs:118)
OC.Components.Cylinder.OnEnable () (at Runtime/Scripts/Components/Cylinder.cs:82)
Steps to reproduce
- Create a
Cylinder and a Gripper (or GripperFixedJoint) in the same scene.
- On the gripper, enable Dynamic Size and set a non-zero Additional Collider Size.
- In the Inspector, wire the cylinder's
OnLimitMaxEvent (or OnLimitMinEvent) to GripperBase.Pick().
- Make sure the cylinder starts at that limit, so the property already holds
true.
- Enter Play mode.
Expected: the gripper picks and resizes its collider without errors.
Actual: NullReferenceException on the first frame; the collider is never resized and _isActive is left true with an inconsistent collider state.
Repro is intermittent — it depends on whether GripperBase.OnEnable (which subscribes OnIsActiveChanged) happened to run before Cylinder.OnEnable. Unity does not guarantee that order between components.
Root cause
GripperBase.Start() (GripperBase.cs:62-67) is the only place that calls GetReferences() and caches _collider, _initColliderSize, _initColliderCenter.
Property.Subscribe (Property.cs:115-119) invokes the action immediately with the current value, so Cylinder.OnEnable can drive gripper logic during the enable phase.
- Unity guarantees all
OnEnable calls precede any Start, so the pick path reaches SetColliderSize with _collider == null (GripperBase.cs:185).
Related defect in the same file
GripperBase.OnDisable() (GripperBase.cs:55-60) uses += where it should use -=:
private new void OnDisable()
{
base.OnDisable();
_isActive.OnValueChanged += OnIsActiveChanged; // should be -=
_isPicked.OnValueChanged += OnIsPickedChanged; // should be -=
}
Handlers are therefore never unsubscribed and are duplicated on every disable/enable cycle, causing OnIsActiveChangedEvent / OnIsPickedChangedEvent to fire multiple times per state change.
Suggested fix
- Move the reference/initial-size caching from
Start() into Awake() so _collider is valid before any OnEnable runs.
- Correct
OnDisable() to unsubscribe with -=.
- Optionally, guard
SetColliderSize against a null _collider as defense in depth.
Environment
- Package:
com.open-commissioning.core (observed at commit b842bcd4aae0)
- Unity: 6.3 (6000.3)
Description
GripperBase.SetColliderSizethrows aNullReferenceExceptionwhenPick()is invoked during theOnEnablephase — beforeGripperBase.Start()has cached theBoxColliderreference.This happens whenever a
Cylinderend-position event (OnLimitMinEvent/OnLimitMaxEvent) is wired in the Inspector toGripperBase.Pick()and the gripper hasDynamic Sizeenabled.Property.Subscribefires the handler immediately with the current value, so the whole pick chain executes insideCylinder.OnEnable().Stack trace
Steps to reproduce
Cylinderand aGripper(orGripperFixedJoint) in the same scene.OnLimitMaxEvent(orOnLimitMinEvent) toGripperBase.Pick().true.Expected: the gripper picks and resizes its collider without errors.
Actual:
NullReferenceExceptionon the first frame; the collider is never resized and_isActiveis lefttruewith an inconsistent collider state.Repro is intermittent — it depends on whether
GripperBase.OnEnable(which subscribesOnIsActiveChanged) happened to run beforeCylinder.OnEnable. Unity does not guarantee that order between components.Root cause
GripperBase.Start()(GripperBase.cs:62-67) is the only place that callsGetReferences()and caches_collider,_initColliderSize,_initColliderCenter.Property.Subscribe(Property.cs:115-119) invokes the action immediately with the current value, soCylinder.OnEnablecan drive gripper logic during the enable phase.OnEnablecalls precede anyStart, so the pick path reachesSetColliderSizewith_collider == null(GripperBase.cs:185).Related defect in the same file
GripperBase.OnDisable()(GripperBase.cs:55-60) uses+=where it should use-=:Handlers are therefore never unsubscribed and are duplicated on every disable/enable cycle, causing
OnIsActiveChangedEvent/OnIsPickedChangedEventto fire multiple times per state change.Suggested fix
Start()intoAwake()so_collideris valid before anyOnEnableruns.OnDisable()to unsubscribe with-=.SetColliderSizeagainst a null_collideras defense in depth.Environment
com.open-commissioning.core(observed at commitb842bcd4aae0)