{"record":{"id":"9d58ba2db766e444","repo":"d2phap/ImageGlass","slug":"button-id-required","errorCode":null,"errorMessage":"Button ID required.","messagePattern":"Button ID required\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"warning","filePath":"v9/ImageGlass/FrmSettings.cs","lineNumber":245,"sourceCode":"            Web2.PostWeb2Message(e.Name, json);\n        }\n        else if (e.Name.Equals(\"Btn_AddCustomToolbarButton_ValidateJson_Create\", StringComparison.Ordinal)\n            || e.Name.Equals(\"Btn_AddCustomToolbarButton_ValidateJson_Edit\", StringComparison.Ordinal))\n        {\n            var isCreate = e.Name.Equals(\"Btn_AddCustomToolbarButton_ValidateJson_Create\", StringComparison.Ordinal);\n            var isValid = true;\n\n            try\n            {\n                // try parsing the json\n                var btn = BHelper.ParseJson<ToolbarItemModel>(e.Data);\n\n                if (btn.Type == ToolbarItemModelType.Button)\n                {\n                    var langPath = $\"{nameof(FrmSettings)}.Toolbar._Errors\";\n                    if (string.IsNullOrWhiteSpace(btn.Id))\n                    {\n                        throw new ArgumentException(Config.Language[$\"{langPath}._ButtonIdRequired\"], nameof(btn.Id));\n                    }\n\n                    if (isCreate\n                        && Config.ToolbarButtons.Any(i => i.Id.Equals(btn.Id, StringComparison.OrdinalIgnoreCase)))\n                    {\n                        throw new ArgumentException(ZString.Format(Config.Language[$\"{langPath}._ButtonIdDuplicated\"], btn.Id), nameof(btn.Id));\n                    }\n\n                    if (string.IsNullOrEmpty(btn.OnClick.Executable))\n                    {\n                        throw new ArgumentException(Config.Language[$\"{langPath}._ButtonExecutableRequired\"], nameof(btn.OnClick.Executable));\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                _ = Config.ShowError(this, title: Config.Language[\"_._Error\"], heading: ex.Message);\n                isValid = false;","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/d2phap/ImageGlass/blob/4a3c4feceffc5a8bb5e56ba836509634aaae47a9/v9/ImageGlass/FrmSettings.cs#L227-L263","documentation":"Thrown in FrmSettings (Tab Toolbar handler) when a custom toolbar button JSON is parsed and btn.Type == ToolbarItemModelType.Button but btn.Id is null, empty, or whitespace. It is an ArgumentException with paramName 'btn.Id'; the message comes from the localized resource FrmSettings.Toolbar._Errors._ButtonIdRequired. The handler is invoked for the Web2 messages 'Btn_AddCustomToolbarButton_ValidateJson_Create' and '..._Edit', and the resulting exception is caught and shown to the user via Config.ShowError.","triggerScenarios":"The settings Web2 UI submits a ToolbarItemModel JSON with Type 'Button' and a missing/blank Id; the JSON is created by hand or by a faulty template that omits the Id field; or the field is present but empty/whitespace.","commonSituations":"A user pastes a hand-written toolbar button config without an Id; a migration/import drops the Id; or the front-end form lets the submit through without client-side validation of the Id field.","solutions":["Provide a non-empty, unique Id in the ToolbarItemModel JSON before submitting.","Add client-side validation in the Web2 toolbar form so the submit button is disabled until Id is non-blank.","If building the model in C#, set btn.Id to a stable slug before validation runs.","Use the default button template and only override Id rather than authoring the whole object from scratch."],"exampleFix":"// before\nvar btn = new ToolbarItemModel\n{\n    Type = ToolbarItemModelType.Button,\n    // Id missing\n    OnClick = new ToolbarClickAction { Executable = \"notepad.exe\" },\n};\n\n// after\nvar btn = new ToolbarItemModel\n{\n    Type = ToolbarItemModelType.Button,\n    Id = \"open-notepad\",               // required, must be unique\n    OnClick = new ToolbarClickAction { Executable = \"notepad.exe\" },\n};","handlingStrategy":"validation","validationCode":"// Validate the parsed model before the settings handler ever throws\nstatic bool IsValidButton(ToolbarItemModel btn, out string error)\n{\n    error = null;\n    if (btn == null) { error = \"No button payload.\"; return false; }\n    if (btn.Type != ToolbarItemModelType.Button) return true;\n    if (string.IsNullOrWhiteSpace(btn.Id))\n    {\n        error = \"Button ID is required.\";\n        return false;\n    }\n    return true;\n}","typeGuard":"static bool IsCompleteButton(ToolbarItemModel btn) =>\n    btn != null\n    && btn.Type == ToolbarItemModelType.Button\n    && !string.IsNullOrWhiteSpace(btn.Id);","tryCatchPattern":"try\n{\n    var btn = BHelper.ParseJson<ToolbarItemModel>(e.Data);\n    // ...validation...\n}\ncatch (ArgumentException ex) when (ex.ParamName == \"btn.Id\")\n{\n    // The handler already surfaces this via Config.ShowError; treat as non-fatal form error.\n    isValid = false;\n}","preventionTips":["Make the Web2 toolbar form require a non-empty Id before enabling submit.","Always author ToolbarItemModel from a known-good template that includes Id.","Validate the model in C# before the settings handler to give an earlier, clearer error.","Treat the Id as a stable slug set once and never blanked on edit."],"tags":["toolbar","validation","settings","winforms","web2","csharp"],"backgroundTag":null,"analyzedSha":"4a3c4feceffc5a8bb5e56ba836509634aaae47a9","analyzedAt":"2026-08-13T16:58:15.523Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}