{"record":{"id":"68f2f8a50e11cdd9","repo":"thebookisclosed/ViVe","slug":"userpolicy-priority-overrides-do-not-support-persisting","errorCode":null,"errorMessage":"UserPolicy priority overrides do not support persisting properties other than EnabledState.","messagePattern":"UserPolicy priority overrides do not support persisting properties other than EnabledState\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"ViVe/FeatureManager.cs","lineNumber":96,"sourceCode":"\n        public static ulong QueryFeatureConfigurationChangeStamp()\n        {\n            return Ntdll.RtlQueryFeatureConfigurationChangeStamp();\n        }\n\n        public static int SetFeatureConfigurations(RTL_FEATURE_CONFIGURATION_UPDATE[] updates, RTL_FEATURE_CONFIGURATION_TYPE configurationType = RTL_FEATURE_CONFIGURATION_TYPE.Runtime)\n        {\n            ulong dummy = 0;\n            return SetFeatureConfigurations(updates, configurationType, ref dummy);\n        }\n\n        public static int SetFeatureConfigurations(RTL_FEATURE_CONFIGURATION_UPDATE[] updates, RTL_FEATURE_CONFIGURATION_TYPE configurationType, ref ulong previousChangeStamp)\n        {\n            foreach (var update in updates)\n                if (ImmutablePriorities.Contains(update.Priority))\n                    throw new ArgumentException(string.Format(\"{0} ({1}) is an immutable priority and can't be written to.\", update.Priority, (int)update.Priority));\n                else if (update.Priority == RTL_FEATURE_CONFIGURATION_PRIORITY.UserPolicy && !update.UserPolicyPriorityCompatible)\n                    throw new ArgumentException(\"UserPolicy priority overrides do not support persisting properties other than EnabledState.\");\n\n            if (configurationType == RTL_FEATURE_CONFIGURATION_TYPE.Runtime)\n                return Ntdll.RtlSetFeatureConfigurations(ref previousChangeStamp, RTL_FEATURE_CONFIGURATION_TYPE.Runtime, updates, updates.Length);\n            else\n                return SetFeatureConfigurationsInRegistry(updates, previousChangeStamp);\n        }\n\n        public static IntPtr RegisterFeatureConfigurationChangeNotification(FeatureConfigurationChangeCallback callback)\n        {\n            return RegisterFeatureConfigurationChangeNotification(callback, IntPtr.Zero);\n        }\n\n        public static IntPtr RegisterFeatureConfigurationChangeNotification(FeatureConfigurationChangeCallback callback, IntPtr context)\n        {\n            Ntdll.RtlRegisterFeatureConfigurationChangeNotification(callback, context, IntPtr.Zero, out IntPtr sub);\n            return sub;\n        }\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/thebookisclosed/ViVe/blob/3f8c6a3425983412da1e8b26cd757c3aa17b3f25/ViVe/FeatureManager.cs#L78-L114","documentation":"When an update targets the UserPolicy priority, ViVe enforces the OS constraint that only the EnabledState property may be persisted at that level; any other property (Variant, VariantPayloadKind, etc.) makes the update invalid and it throws ArgumentException before calling the native API. This mirrors a check the Windows kernel/ntdll performs, surfaced early as a .NET exception.","triggerScenarios":"Calling SetFeatureConfigurations with an update whose Priority == RTL_FEATURE_CONFIGURATION_PRIORITY.UserPolicy and whose UserPolicyPriorityCompatible flag is false, meaning non-EnabledState properties are set and would be persisted.","commonSituations":"Trying to persist variant/payload data alongside a user-policy override; reusing a runtime-priority update struct and merely switching Priority to UserPolicy without clearing other properties or setting UserPolicyPriorityCompatible.","solutions":["Set update.UserPolicyPriorityCompatible = true and ensure only EnabledState is meaningful, or","Move Variant/VariantPayloadKind and other properties onto an update with a non-UserPolicy priority","Split the change into two updates: one UserPolicy update carrying only EnabledState, plus a separate update at another writable priority for other properties"],"exampleFix":"// before\nvar u = new RTL_FEATURE_CONFIGURATION_UPDATE { FeatureId = id, Priority = RTL_FEATURE_CONFIGURATION_PRIORITY.UserPolicy, Variant = 3 }; // incompatible\n// after\nvar u = new RTL_FEATURE_CONFIGURATION_UPDATE { FeatureId = id, Priority = RTL_FEATURE_CONFIGURATION_PRIORITY.UserPolicy, EnabledState = RTL_FEATURE_ENABLED_STATE.Enabled, UserPolicyPriorityCompatible = true };","handlingStrategy":"validation","validationCode":"if (update.Priority == RTL_FEATURE_CONFIGURATION_PRIORITY.UserPolicy && !update.UserPolicyPriorityCompatible)\n    throw new InvalidOperationException(\"UserPolicy updates may only persist EnabledState; set UserPolicyPriorityCompatible or move other properties to another priority.\");","typeGuard":"static bool IsUserPolicyCompatible(RTL_FEATURE_CONFIGURATION_UPDATE u) =>\n    u.Priority != RTL_FEATURE_CONFIGURATION_PRIORITY.UserPolicy || u.UserPolicyPriorityCompatible;","tryCatchPattern":"try\n{\n    ViVe.Feature.FeatureManager.SetFeatureConfigurations(updates, configType, ref stamp);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"UserPolicy\"))\n{\n    // split the update or set UserPolicyPriorityCompatible and retry\n}","preventionTips":["Set UserPolicyPriorityCompatible = true when using UserPolicy updates with only EnabledState","Keep Variant/VariantPayloadKind changes in separate updates at other priorities","Reuse of read-back update structs: always re-check the UserPolicy constraint"],"tags":["csharp","windows","feature-management","argument-validation"],"backgroundTag":"conflicting-config-options","analyzedSha":"3f8c6a3425983412da1e8b26cd757c3aa17b3f25","analyzedAt":"2026-09-14T11:10:27.021Z","contentChangedAt":"2026-09-14T11:10:27.021Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}