File-New-Project/EarTrumpet · error · NotImplementedException
Missing resource: {res}
Error message
Missing resource: {res} What it means
PartViewModel is the base class for every trigger/condition/action view model in the EarTrumpet.Actions addon. Its AddText and LinkText properties both call ResolveResource(suffix), forming the key "{Part.GetType().Name}_{suffix}" against the Actions resx. Because every concrete Part-derived data type flows through this base, each one must ship both "{TypeName}_AddText" and "{TypeName}_LinkText" resource strings. A null/whitespace lookup throws NotImplementedException, which is a hard failure during UI binding.
Source
Thrown at EarTrumpet/Addons/EarTrumpet.Actions/ViewModel/PartViewModel.cs:75
Part = part;
}
protected void Attach(INotifyPropertyChanged obj)
{
obj.PropertyChanged += (s, e) =>
{
RaisePropertyChanged(e.PropertyName);
RaisePropertyChanged(nameof(LinkText));
};
}
private string ResolveResource(string suffix)
{
var res = $"{Part.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
- For the offending Part type name (shown in the thrown message), add both "{TypeName}_AddText" and "{TypeName}_LinkText" to Resources.resx and each localized resx.
- Audit all Part-derived types (search for ': Part' / ': BaseTrigger' / ': BaseCondition' / ': BaseAction') and ensure each has AddText + LinkText resx entries.
- If you renamed the type, update the resx key prefix to match the new GetType().Name.
- Add a unit test that reflects over all Part subtypes and asserts both keys resolve non-empty.
Example fix
// before: new action type has no resx strings
// public class SetDefaultDeviceAction : BaseAction { }
// after: add resx entries
// <data name="SetDefaultDeviceAction_AddText"><value>Set as default</value></data>
// <data name="SetDefaultDeviceAction_LinkText"><value>device</value></data> Defensive patterns
Strategy: validation
Validate before calling
var key = $"{Part.GetType().Name}_{suffix}";
var value = Properties.Resources.ResourceManager.GetString(key);
if (string.IsNullOrWhiteSpace(value)) { Trace.WriteLine($"Missing resource: {key}"); return string.Empty; } Try / catch
try { return ResolveResource(suffix); } catch (NotImplementedException ex) { Trace.WriteLine(ex); return string.Empty; } Prevention
- When adding a Part subtype, add AddText + LinkText resx entries in the same commit.
- CI: reflect over all Part-derived types and assert both keys exist in the resx.
- Never rename a Part type without a matching resx key rename.
When it happens
Trigger: Any XAML binding or code access to AddText / LinkText on a PartViewModel whose wrapped Part runtime type lacks one of the resx keys. E.g. a new action type 'SetDefaultDeviceAction' is rendered before "{SetDefaultDeviceAction}_LinkText" exists.
Common situations: Adding a new Part subclass (new action/condition/trigger) without adding its two resx entries; renaming a Part type without mirroring the resx; partial localized resx missing the keys for some parts.
Related errors
AI-assisted analysis of File-New-Project/EarTrumpet@aa894e51c2 (2026-08-13).
Data as JSON: /api/errors/647f209f5da82245.
Report an issue: GitHub.