{"record":{"id":"55bad2abdd17d5e4","repo":"File-New-Project/EarTrumpet","slug":"missing-resource-res-55bad2","errorCode":null,"errorMessage":"Missing resource: {res}","messagePattern":"Missing resource: (.+?)","errorType":"exception","errorClass":"NotImplementedException","httpStatus":null,"severity":"error","filePath":"EarTrumpet/Addons/EarTrumpet.Actions/ViewModel/TextViewModel.cs","lineNumber":45,"sourceCode":"        public override string ToString()\r\n        {\r\n            if (string.IsNullOrWhiteSpace(_part.Text))\r\n            {\r\n                return ResolveResource(\"EmptyText\");\r\n            }\r\n            else\r\n            { \r\n                return _part.Text;\r\n            }\r\n        }\r\n\r\n        private string ResolveResource(string suffix)\r\n        {\r\n            var res = $\"{_part.GetType().Name}_{suffix}\";\r\n            var ret = Properties.Resources.ResourceManager.GetString(res);\r\n            if (string.IsNullOrWhiteSpace(ret))\r\n            {\r\n                throw new NotImplementedException($\"Missing resource: {res}\");\r\n            }\r\n            return ret;\r\n        }\r\n    }\r\n}\r\n","sourceCodeStart":27,"sourceCodeEnd":51,"githubUrl":"https://github.com/File-New-Project/EarTrumpet/blob/aa894e51c22f5f9a939b31b224c4d2d3e163416e/EarTrumpet/Addons/EarTrumpet.Actions/ViewModel/TextViewModel.cs#L27-L51","documentation":"TextViewModel wraps an IPartWithText and exposes PromptText (always) and EmptyText (from ToString when the part's Text is blank). ResolveResource builds \"{_part.GetType().Name}_{suffix}\", so the CONCRETE runtime type behind the IPartWithText interface must supply \"{ConcreteType}_PromptText\" and \"{ConcreteType}_EmptyText\" resx strings. Missing either throws NotImplementedException.","triggerScenarios":"The UI binds PromptText (always evaluated) or calls ToString() when _part.Text is empty, and the concrete part type name lacks the corresponding resx key. The interface IPartWithText hides the real type, so the failure surfaces only at runtime for whichever implementation is active.","commonSituations":"A new IPartWithText implementer was added without PromptText/EmptyText resx entries; the implementing class was renamed; a localized resx omits the keys.","solutions":["Identify the concrete _part type name (from the thrown message) and add \"{TypeName}_PromptText\" and \"{TypeName}_EmptyText\" to Resources.resx and localized resx files.","Enumerate all IPartWithText implementations and confirm each has both keys.","Match the resx key prefix to the exact GetType().Name after any rename.","Rebuild so the resx is recompiled into the assembly."],"exampleFix":"// before: IPartWithText impl 'PromptTextPart' has no strings\n// after resx:\n//   <data name=\"PromptTextPart_PromptText\"><value>Enter text</value></data>\n//   <data name=\"PromptTextPart_EmptyText\"><value>(empty)</value></data>","handlingStrategy":"validation","validationCode":"var key = $\"{_part.GetType().Name}_{suffix}\";\nvar value = Properties.Resources.ResourceManager.GetString(key);\nif (string.IsNullOrWhiteSpace(value)) { Trace.WriteLine($\"Missing resource: {key}\"); return string.Empty; }","typeGuard":null,"tryCatchPattern":"try { return ResolveResource(suffix); } catch (NotImplementedException ex) { Trace.WriteLine(ex); return string.Empty; }","preventionTips":["Because IPartWithText hides the concrete type, enumerate every implementer in a test and assert its PromptText/EmptyText keys.","Add resx entries whenever a new IPartWithText implementation is introduced.","Keep resx key prefixes aligned with GetType().Name after renames."],"tags":["resource","localization","eartrumpet-actions","resx","viewmodel","ipartwithtext"],"backgroundTag":null,"analyzedSha":"aa894e51c22f5f9a939b31b224c4d2d3e163416e","analyzedAt":"2026-08-13T18:51:30.564Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}