{"record":{"id":"5efefe71261031ca","repo":"iOfficeAI/OfficeCLI","slug":"set-command-requires-props-field-with-at-least","errorCode":null,"errorMessage":"'set' command requires 'props' field with at least one key=value. Got empty/missing props.","messagePattern":"'set' command requires 'props' field with at least one key=value\\. Got empty/missing props\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/CommandBuilder.cs","lineNumber":988,"sourceCode":"                        ? OfficeCli.Handlers.ExcelHandler.ResolveCellAttributeAlias : null;\n                var (results, warnings) = OfficeCli.Core.AttributeFilter.FilterSelector(selector, handler.Query, keyResolver);\n                if (item.Text is { } textFilter && !string.IsNullOrEmpty(textFilter))\n                    // MatchesTextFilter (not plain Contains) so a batch query\n                    // text filter honours r\"regex\" like the CLI and resident do.\n                    results = results.Where(n => n.Text != null && OfficeCli.Core.AttributeFilter.MatchesTextFilter(n.Text, textFilter)).ToList();\n                foreach (var w in warnings) Console.Error.WriteLine(w.Message);\n                return OfficeCli.Core.OutputFormatter.FormatNodes(results, format);\n            }\n            case \"set\":\n            {\n                if (string.IsNullOrEmpty(item.Path))\n                    throw new ArgumentException(\"'set' command requires 'path' field. Example: {\\\"command\\\": \\\"set\\\", \\\"path\\\": \\\"/slide[1]\\\", \\\"props\\\": {\\\"bold\\\": \\\"true\\\"}}\");\n                // Match standalone `set` rejection of empty/missing props — a\n                // batch step with no props is a no-op that previously reported\n                // success, hiding caller mistakes (forgotten props field,\n                // misspelled key promoted to root, etc.).\n                if (props.Count == 0)\n                    throw new ArgumentException(\"'set' command requires 'props' field with at least one key=value. Got empty/missing props.\");\n                var path = item.Path;\n                OfficeCli.Core.MutationSelectorGuard.EnsureScoped(path, \"set\");\n                // Shared core: apply + prop-autocorrect + categorise. Identical\n                // across CLI / batch / MCP / resident; only the output below is\n                // batch-specific.\n                var (applied, unsupported, autoCorrected) = ApplySetWithCorrection(handler, path, props);\n                var parts = new List<string>();\n                if (autoCorrected.Count > 0)\n                    parts.Add(\"Auto-corrected: \" + string.Join(\", \", autoCorrected.Select(ac => $\"{ac.Original}→{ac.Corrected}\")));\n                if (applied.Count > 0)\n                {\n                    var msg = $\"Updated {path}: {string.Join(\", \", applied.Select(kv => $\"{kv.Key}={kv.Value}\"))}\";\n                    if (props.ContainsKey(\"find\"))\n                    {\n                        var matched = handler switch\n                        {\n                            OfficeCli.Handlers.WordHandler wh => wh.LastFindMatchCount,\n                            OfficeCli.Handlers.PowerPointHandler ph => ph.LastFindMatchCount,","sourceCodeStart":970,"sourceCodeEnd":1006,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/CommandBuilder.cs#L970-L1006","documentation":"Thrown by ExecuteBatchItem for a batch \"set\" step that has a path but empty or missing props. Previously this was a silent no-op reported as success, hiding caller mistakes (forgotten props field, a key promoted to the root, an empty generated dict). It now fails fast to match the standalone set command's behavior.","triggerScenarios":"{\"command\":\"set\",\"path\":\"/slide[1]\",\"props\":{}}; {\"command\":\"set\",\"path\":\"/slide[1]\"} with no props field; props built dynamically that produced an empty dictionary.","commonSituations":"Props field misspelled or promoted to the root object; a generator that filtered out all keys; conditional logic that left props empty; an LLM that emitted path but omitted props.","solutions":["Supply at least one key=value in props: {\"bold\":\"true\"}.","If no props apply, skip the set step entirely rather than emitting an empty one.","Guard the generator: only emit a set step when the props dict is non-empty.","Check for misspelled prop keys that were meant to be inside props."],"exampleFix":"// before\n{\"command\":\"set\",\"path\":\"/slide[1]\",\"props\":{}}\n// after\n{\"command\":\"set\",\"path\":\"/slide[1]\",\"props\":{\"bold\":\"true\"}}","handlingStrategy":"validation","validationCode":"if ((item.Props?.Count ?? 0) == 0)\n    throw new ArgumentException(\"'set' requires at least one prop\");\n// or skip emitting the step when props is empty","typeGuard":"static bool IsValidSetItem(BatchItem i)\n    => !string.IsNullOrEmpty(i.Path) && (i.Props?.Count ?? 0) > 0;","tryCatchPattern":null,"preventionTips":["Skip set steps that would have empty props rather than emitting them.","Only emit a set step when the props dict is non-empty.","Check for keys accidentally promoted out of props."],"tags":["batch","set","validation","officecli"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}