{"record":{"id":"a3df335b60b77691","repo":"iOfficeAI/OfficeCLI","slug":"unknown-chart-preset-value-available-string","errorCode":null,"errorMessage":"Unknown chart preset '{value}'. Available: {string.Join(\", \", ChartPresets.PresetNames)}.","messagePattern":"Unknown chart preset '(.+?)'\\. Available: (.+?)\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/Chart/ChartHelper.Setter.cs","lineNumber":272,"sourceCode":"            // majortickmark/ticklabelpos setters so those can skip the now-hidden\n            // axis; otherwise the hidden secondary catAx reappears after replay.\n            if (lower is \"secondaryaxis\" or \"secondary\") return 1;\n            if (lower is \"title\" or \"legend\" or \"datalabels\" or \"labels\") return 1;\n            // axis-title TEXT must build the <c:title> element before axistitle.pPr\n            // (order 2) replaces its paragraph properties.\n            if (lower is \"axistitle\" or \"vtitle\" or \"cattitle\" or \"htitle\") return 1;\n            return 2;\n        }\n        var ordered = properties.OrderBy(kv => PropOrder(kv.Key));\n        foreach (var (key, value) in ordered)\n        {\n            switch (key.ToLowerInvariant())\n            {\n                case \"preset\" or \"style.preset\" or \"theme\":\n                {\n                    var presetProps = ChartPresets.GetPreset(value);\n                    if (presetProps == null)\n                        throw new ArgumentException(\n                            $\"Unknown chart preset '{value}'. Available: {string.Join(\", \", ChartPresets.PresetNames)}.\");\n                    // Recursively apply preset properties\n                    var presetUnsupported = SetChartProperties(chartPart, presetProps);\n                    // Silently skip title.* properties when chart has no title —\n                    // presets include title styling but charts may legitimately have no title\n                    var hasTitle = chart.GetFirstChild<C.Title>() != null;\n                    if (!hasTitle)\n                        presetUnsupported.RemoveAll(k => k.StartsWith(\"title.\", StringComparison.OrdinalIgnoreCase)\n                            || (k.StartsWith(\"title\", StringComparison.OrdinalIgnoreCase) && k.Length > 5));\n                    unsupported.AddRange(presetUnsupported);\n                    break;\n                }\n\n                case \"title\":\n                    chart.RemoveAllChildren<C.Title>();\n                    if (!string.IsNullOrEmpty(value) && !value.Equals(\"none\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        // CONSISTENCY(autoTitleDeleted-paired): setting a title back","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/Chart/ChartHelper.Setter.cs#L254-L290","documentation":"Thrown by SetChartProperties when the 'preset', 'style.preset', or 'theme' key resolves to a name not registered in ChartPresets (the closed set: minimal, dark, corporate, magazine, dashboard, colorful, monochrome). The library applies a preset by recursively expanding its property dictionary before the rest of the Set call, so an unknown name cannot be silently skipped. Rejecting up front prevents a half-applied preset leaking into the chart XML.","triggerScenarios":"Calling SetChartProperties with { [\"preset\"] = \"miniml\" } (typo), { [\"theme\"] = \"dark2\" }, or any value not in ChartPresets.PresetNames. The alias 'mono' is accepted for monochrome; no other aliases exist.","commonSituations":"Typos in config files or CLI flags preset=minimal; outdated docs referencing a preset name that was renamed/removed in a version change; copy-pasting a preset name from a different tool's vocabulary.","solutions":["Spell the preset name exactly as one of: minimal, dark, corporate, magazine, dashboard, colorful, monochrome (or mono).","Read the allowed list programmatically from ChartPresets.PresetNames before building your property dictionary.","If you intended a custom look, do not use the preset key — set the individual properties (colors, gridlines, fonts) directly."],"exampleFix":"// before\nSetChartProperties(part, new() { [\"preset\"] = \"miniml\" });\n// after\nvar name = \"minimal\";\nif (!ChartPresets.PresetNames.Contains(name))\n    throw new InvalidOperationException($\"Unknown preset '{name}'\");\nSetChartProperties(part, new() { [\"preset\"] = name });","handlingStrategy":"validation","validationCode":"static readonly HashSet<string> ChartPresetNames =\n    new(ChartPresets.PresetNames, StringComparer.OrdinalIgnoreCase) { \"mono\" };\n\nstatic bool IsValidPreset(string v) =>\n    !string.IsNullOrWhiteSpace(v) && ChartPresetNames.Contains(v.Trim());","typeGuard":"static bool IsKnownPreset(string v) =>\n    ChartPresets.PresetNames.Contains(v, StringComparer.OrdinalIgnoreCase)\n    || v.Equals(\"mono\", StringComparison.OrdinalIgnoreCase);","tryCatchPattern":"try { SetChartProperties(part, props); }\ncatch (ArgumentException ex) when (ex.Message.StartsWith(\"Unknown chart preset\"))\n{ /* log and surface available preset names to the user */ }","preventionTips":["Build preset pickers from ChartPresets.PresetNames so the UI cannot emit an unknown name.","Treat 'mono' as the only documented alias for monochrome.","Validate before composing the property dictionary, not after the throw."],"tags":["ooxml","chart","input-validation","preset","csharp"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}