dotnet/wpf · error · InvalidEnumArgumentException

InvalidEnumArgumentException(nameof(value), (int)modifiers…

Error message

InvalidEnumArgumentException(nameof(value), (int)modifiers, typeof(ModifierKeys))

What it means

ModifierKeysConverter.ConvertTo converts a ModifierKeys value to its string representation (e.g. 'Control+Alt'). Before converting it verifies the value is one of the defined ModifierKeys enum values; if the boxed value is not a valid ModifierKeys, an InvalidEnumArgumentException is thrown. This guards the converter against foreign or out-of-range values passed to the type conversion pipeline.

Solutions

  1. Validate the value is a defined ModifierKeys member before calling ConvertTo (e.g. Enum.IsDefined(typeof(ModifierKeys), value)).
  2. Ensure the object passed in is actually a ModifierKeys instance, not a raw int or another enum.
  3. Check the destinationType parameter is typeof(string).
  4. Catch InvalidEnumArgumentException at the conversion boundary and substitute ModifierKeys.None or a fallback rendering.

Example fix

// before
var text = converter.ConvertTo(keyValue, typeof(string)); // keyValue may be (ModifierKeys)999
// after
if (Enum.IsDefined(typeof(ModifierKeys), keyValue))
    text = converter.ConvertTo(keyValue, typeof(string));
else
    text = converter.ConvertTo(ModifierKeys.None, typeof(string));
Defensive patterns

Strategy: validation

Validate before calling

bool canConvert = value is ModifierKeys mk && Enum.IsDefined(typeof(ModifierKeys), mk) && destinationType == typeof(string);

Type guard

static bool IsValidModifierKeys(object v) => v is ModifierKeys mk && Enum.IsDefined(typeof(ModifierKeys), mk);

Try / catch

try { return converter.ConvertTo(value, typeof(string)); }
catch (InvalidEnumArgumentException) { return string.Empty; }

Prevention

When it happens

Trigger: Calling ConvertTo with a value that is not a ModifierKeys enum value, a value cast from an arbitrary int that falls outside the defined ModifierKeys set (e.g. (ModifierKeys)999), or a destinationType other than string (that path throws GetConvertToException first).

Common situations: Data binding or XAML serialization pipelines feeding user-supplied integers into the converter; deserializing old/persisted settings where the stored int no longer maps to a ModifierKeys member after enum changes.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/9b7c01361c000c70. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Input/ModifierKeysConverter.cs:109

        /// Converts a <paramref name="value"/> of <see cref="ModifierKeys"/> to its <see langword="string"/> represensation.
        /// </summary>
        /// <param name="context">Serialization Context</param>
        /// <param name="culture">Culture Info</param>
        /// <param name="value">ModifierKeys value</param>
        /// <param name="destinationType">Type to Convert</param>
        /// <returns>A <see langword="string"/> representing the <see cref="ModifierKeys"/> specified by <paramref name="value"/>.</returns>
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            ArgumentNullException.ThrowIfNull(destinationType);

            // We can only convert to string
            if (destinationType != typeof(string))
                throw GetConvertToException(value, destinationType);

            // Check whether value falls within defined set
            ModifierKeys modifiers = (ModifierKeys)value;
            if (!IsDefinedModifierKeys(modifiers))
                throw new InvalidEnumArgumentException(nameof(value), (int)modifiers, typeof(ModifierKeys));

            // This is a fast path for when only a single modifier (or none) is set, which is a very common scenario.
            // Therefore we want a fast path with an allocation free return, taking advantage of interned strings.
            return modifiers switch
            {
                ModifierKeys.None => string.Empty,
                ModifierKeys.Control => "Ctrl",
                ModifierKeys.Alt => "Alt",
                ModifierKeys.Shift => "Shift",
                ModifierKeys.Windows => "Windows",
                // Since we were not able to match a single modifier alone, there must be multiple modifiers involved.
                _ => ConvertMultipleModifiers(modifiers),
            };
        }

        private static string ConvertMultipleModifiers(ModifierKeys modifiers)
        {
            // Ctrl+Alt+Windows+Shift is the maximum char length, though the composition of such value is improbable

View on GitHub (pinned to 81131a70a4)