diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index fbb348f70a7307..428e05ca314752 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -1771,6 +1771,17 @@ void DeviceCommissioner::CleanupCommissioning(DeviceProxy * proxy, NodeId nodeId if (completionStatus.err == CHIP_NO_ERROR) { CommissioneeDeviceProxy * commissionee = FindCommissioneeDevice(nodeId); + // CommissioningStageComplete uses mDeviceBeingCommissioned, which can + // be commissionee if we are cleaning up before we've gone operational. Normally + // that would not happen in this non-error case, _except_ if we were told to skip sending + // CommissioningComplete: in that case we do not have an operational DeviceProxy, so + // we're using our CommissioneeDeviceProxy to do a successful cleanup. + // + // This means we have to call CommissioningStageComplete() before we destroy commissionee. + // + // This should be safe, because CommissioningStageComplete() does not call CleanupCommissioning + // when called in the cleanup stage (which is where we are), and StopPairing does not directly release + // mDeviceBeingCommissioned. CommissioningStageComplete(CHIP_NO_ERROR); if (commissionee != nullptr) {