{"record":{"id":"8181d8a0f4ac6493","repo":"dotnet/wpf","slug":"sr-touch-devicenotactivated","errorCode":null,"errorMessage":"SR.Touch_DeviceNotActivated","messagePattern":"SR\\.Touch_DeviceNotActivated","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/TouchDevice.cs","lineNumber":719,"sourceCode":"\n            if (_activeDevices.Count == 1)\n            {\n                _isPrimary = true;\n            }\n\n            _isActive = true;\n\n            if (Activated != null)\n            {\n                Activated(this, EventArgs.Empty);\n            }\n        }\n\n        protected void Deactivate()\n        {\n            if (!_isActive)\n            {\n                throw new InvalidOperationException(SR.Touch_DeviceNotActivated);\n            }\n\n            Capture(null);\n\n            DetachTouchDevice();\n            RemoveActiveDevice(this);\n\n            _isActive = false;\n            _manipulatingElement = null;\n\n            if (Deactivated != null)\n            {\n                Deactivated(this, EventArgs.Empty);\n            }\n        }\n\n        /// <summary>\n        ///     Forces the TouchDevice to resynchronize.","sourceCodeStart":701,"sourceCodeEnd":737,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/TouchDevice.cs#L701-L737","documentation":"TouchDevice.Deactivate unregisters the touch device (releases capture, detaches, removes from the active device list). It requires the device to currently be active; calling it when _isActive is false throws InvalidOperationException(Touch_DeviceNotActivated). Deactivate must mirror a prior successful Activate call.","triggerScenarios":"Calling Deactivate() on a TouchDevice that was never activated; calling Deactivate twice (the second call throws); calling Deactivate after the device was already torn down by the system (e.g. the tablet/device went away and deactivate already happened implicitly).","commonSituations":"Cleanup code in a custom touch provider that unconditionally calls Deactivate in Dispose; double disposal (Dispose called from both finalizer and explicit path); asymmetric lifecycle where an error path skipped Activate but cleanup still calls Deactivate.","solutions":["Only call Deactivate from the matching lifecycle path that previously called Activate; keep a boolean to make cleanup idempotent.","In Dispose patterns, guard Deactivate with the same _isActive-style flag so double disposal is safe.","Let the WPF input system deactivate the device itself when the device is removed instead of forcing Deactivate."],"exampleFix":"// before\npublic void Dispose()\n{\n    Deactivate(); // throws if never activated or already deactivated\n}\n\n// after\nprivate bool _activated;\npublic void Dispose()\n{\n    if (_activated)\n    {\n        Deactivate();\n        _activated = false;\n    }\n}","handlingStrategy":"validation","validationCode":"if (!device.IsActive) return; // nothing to deactivate","typeGuard":null,"tryCatchPattern":"try { device.Deactivate(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"activated\"))\n{\n    // not active — nothing to do\n}","preventionTips":["Make cleanup idempotent with an _activated boolean.","Only deactivate from the path that activated the device.","Use try/finally around Activate/Deactivate pairs."],"tags":["wpf","touch","invalid-operation","state-machine"],"backgroundTag":"invalid-state-transition","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}