File-New-Project/EarTrumpet · error · NotImplementedException

Missing resource: {res}

Error message

Missing resource: {res}

What it means

HotkeyViewModel.ResolveResource builds a resource key "{TypeName}_{suffix}" from the runtime type name of the wrapped HotkeyTrigger and looks it up in the EarTrumpet.Actions Properties.Resources (.resx). The only suffix used here is "EmptyText", requested from ToString() when the trigger's hotkey Option is empty. If ResourceManager.GetString returns null or whitespace for the key "HotkeyTrigger_EmptyText", the method treats the resource contract as broken and throws NotImplementedException. This is a localization/content invariant: every Trigger type name must have a matching resx entry.

Source

Thrown at EarTrumpet/Addons/EarTrumpet.Actions/ViewModel/HotkeyViewModel.cs:40

        public override string ToString()
        {
            if (_trigger.Option.IsEmpty)
            {
                return ResolveResource("EmptyText");
            }
            else
            {
                return _trigger.Option.ToString();
            }
        }

        private string ResolveResource(string suffix)
        {
            var res = $"{_trigger.GetType().Name}_{suffix}";
            var ret = Properties.Resources.ResourceManager.GetString(res);
            if (string.IsNullOrWhiteSpace(ret))
            {
                throw new NotImplementedException($"Missing resource: {res}");
            }
            return ret;
        }
    }
}

View on GitHub (pinned to aa894e51c2)

Solutions

  1. Open EarTrumpet/Addons/EarTrumpet.Actions/Properties/Resources.resx (and any .<culture>.resx) and add a data entry named "HotkeyTrigger_EmptyText" with the placeholder text (e.g. "Select a hotkey").
  2. If you renamed the HotkeyTrigger type, either restore the old name or add resx entries whose prefix matches the new type name exactly (the code uses GetType().Name).
  3. Rebuild the project so Resources.Designer.cs / ResourceManager regenerates and the new key is embedded.
  4. Verify with a quick grep that every GetType().Name produced by this code path has all used suffixes (EmptyText) present in the resx.

Example fix

// before (resx missing the key)
//   <data name="HotkeyTrigger_EmptyText"> ... absent ...

// after: add to Resources.resx
//   <data name="HotkeyTrigger_EmptyText" xml:space="preserve">
//     <value>Select a hotkey</value>
//   </data>
Defensive patterns

Strategy: validation

Validate before calling

// Run before relying on the resource
var key = $"{trigger.GetType().Name}_EmptyText";
var value = Properties.Resources.ResourceManager.GetString(key);
if (string.IsNullOrWhiteSpace(value))
{
    // missing resource: log and supply a safe default instead of throwing
    Trace.WriteLine($"Missing resource: {key}");
    return string.Empty;
}

Try / catch

try { return ResolveResource("EmptyText"); } catch (NotImplementedException ex) { Trace.WriteLine(ex); return string.Empty; }

Prevention

When it happens

Trigger: The UI calls HotkeyViewModel.ToString() (e.g. while rendering the Actions editor list) while _trigger.Option.IsEmpty is true, so ResolveResource("EmptyText") runs. The key "HotkeyTrigger_EmptyText" is absent from the EarTrumpet.Actions resx (neutral or current culture), so GetString returns null and the throw fires.

Common situations: A developer renamed the HotkeyTrigger class but did not update the resx key prefix; a localized satellite resx omits the key; the .resx code generator did not run after a resx edit; a new trigger variant was introduced without its display strings.

Related errors


AI-assisted analysis of File-New-Project/EarTrumpet@aa894e51c2 (2026-08-13). Data as JSON: /api/errors/2a71836128b43d99. Report an issue: GitHub.