tui-cs/Terminal.Gui · error · InvalidOperationException

Setting Values directly is not allowed.

Error message

Setting Values directly is not allowed.

What it means

Thrown by the OptionSelectorTEnum.Values override setter unconditionally — the enum-typed selector deliberately blocks direct writes to the inherited int-based Values collection. For an enum selector, valid values come from the enum members, so allowing arbitrary int lists would break the TEnum mapping. The getter still delegates to base.

Source

Thrown at Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs:38

        base.Values = Enum.GetValues<TEnum> ().Select (f => Convert.ToInt32 (f)).ToList ().AsReadOnly ();

        // Set labels, which triggers CreateSubViews + UpdateChecked.
        Labels = Enum.GetNames<TEnum> ();
    }

    /// <summary>
    ///     Gets or sets the value of the selected option.
    /// </summary>
    public new TEnum? Value
    {
        get => base.Value.HasValue ? (TEnum)Enum.ToObject (typeof (TEnum), base.Value.Value) : null;
        set => base.Value = value.HasValue ? Convert.ToInt32 (value.Value) : null;
    }

    /// <summary>
    ///     Prevents calling the base Values property setter with arbitrary values.
    /// </summary>
    public override IReadOnlyList<int>? Values { get => base.Values; set => throw new InvalidOperationException ("Setting Values directly is not allowed."); }

    /// <summary>
    ///     Raised when <see cref="Value"/> has changed. Provides the new value as <typeparamref name="TEnum"/>?.
    /// </summary>
    public new event EventHandler<EventArgs<TEnum?>>? ValueChanged;

    /// <summary>
    ///     Called when <see cref="Value"/> has changed. Raises the generic <see cref="ValueChanged"/> event.
    /// </summary>
    protected override void OnValueChanged (int? value, int? previousValue)
    {
        base.OnValueChanged (value, previousValue);

        TEnum? newValue = value.HasValue ? (TEnum)Enum.ToObject (typeof (TEnum), value.Value) : null;

        ValueChanged?.Invoke (this, new EventArgs<TEnum?> (newValue));
    }

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Do not set Values on the enum selector — the values are derived from TEnum automatically when you set Options/Source.
  2. If you need a custom value set, use the non-generic OptionSelector (int-based) instead of OptionSelectorTEnum<TEnum>.
  3. Exclude this property from serialisers/binders (make it read-only in the binding map).

Example fix

// before
var sel = new OptionSelectorTEnum<MyEnum>();
sel.Values = new[] { 0, 1, 2 }; // throws 171

// after
var sel = new OptionSelectorTEnum<MyEnum>();
sel.Value = MyEnum.Second; // set selection via the typed Value
// and for custom value sets use the int selector:
var intSel = new OptionSelector { Values = [0,1,2] };
Defensive patterns

Strategy: type-guard

Validate before calling

// Do not assign Values on an enum selector; set Value or use the int-based OptionSelector
if (selector is OptionSelectorTEnum<_>) { /* use .Value only */ }

Type guard

static bool ValuesIsReadOnly(SelectorBase s) => s is OptionSelectorTEnum<_>;

Prevention

When it happens

Trigger: Assigning optionSelectorEnum.Values = someList; in code, or via reflection/serialisation/binder that targets the Values property. Any write attempt fails by design.

Common situations: Generic/binding code that sets Values across selector types without special-casing enums; config/deserialisation trying to populate Values directly; refactor that routed selection through the base property.

Related errors


AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13). Data as JSON: /api/errors/4e749b982863c3ed. Report an issue: GitHub.