iOfficeAI/OfficeCLI · error · ArgumentException

'add' command requires 'type' or 'from' field. Example

Error message

'add' command requires 'type' or 'from' field. Example: {"command": "add", "parent": "/", "type": "slide"}

What it means

Thrown by ExecuteBatchItem for a batch "add" step that has a parent but neither "type" (what kind of element to create) nor "from" (a source path to copy via handler.CopyFrom). At least one of the two is required.

Solutions

  1. Add a type: {"command":"add","parent":"/","type":"slide"}.
  2. Or add a from to copy an existing element: {"command":"add","parent":"/","from":"/slide[1]"}.
  3. Validate every add step has a non-empty type or from before submission.
  4. Ensure type is not an empty string (empty string fails the IsNullOrEmpty check).

Example fix

// before
{"command":"add","parent":"/","type":""}
// after
{"command":"add","parent":"/","type":"slide"}
Defensive patterns

Strategy: validation

Validate before calling

if (string.IsNullOrEmpty(item.Type) && string.IsNullOrEmpty(item.From))
    throw new ArgumentException("'add' requires a non-empty 'type' or 'from'");

Type guard

static bool IsValidAddItem(BatchItem i)
    => !string.IsNullOrEmpty(i.Parent ?? i.Path)
       && (!string.IsNullOrEmpty(i.Type) || !string.IsNullOrEmpty(i.From));

Prevention

When it happens

Trigger: {"command":"add","parent":"/slide[1]"}; an add step where a conditional left both type and from null; type emitted as an empty string.

Common situations: Forgotten type field; conditional logic that failed to set a type; a generator that emitted parent but not type; confusion between create (type) and copy (from).

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/8d69d71184ab776d. Report an issue: GitHub.

Appendix: source

Thrown at src/officecli/CommandBuilder.cs:1057

                    // requested property was rejected (nothing applied), the
                    // step is a failure, not a successful no-op. Without
                    // this, batch swallowed unsupported_property into an inner
                    // success=true while the same set issued via the
                    // standalone set command returned success=false exit 2.
                    // Per-step verdict flips to false; outer batch envelope
                    // still rides on the existing partial-success rule.
                    if (applied.Count == 0)
                        throw new CliException(string.Join("\n", parts)) { Code = "unsupported_property" };
                }
                return string.Join("\n", parts);
            }
            case "add":
            {
                var parentPath = item.Parent ?? item.Path;
                if (string.IsNullOrEmpty(parentPath))
                    throw new ArgumentException("'add' command requires 'parent' field. Example: {\"command\": \"add\", \"parent\": \"/slide[1]\", \"type\": \"shape\", \"props\": {\"text\": \"Hello\"}}");
                if (string.IsNullOrEmpty(item.Type) && string.IsNullOrEmpty(item.From))
                    throw new ArgumentException("'add' command requires 'type' or 'from' field. Example: {\"command\": \"add\", \"parent\": \"/\", \"type\": \"slide\"}");
                InsertPosition? pos = null;
                if (item.Index.HasValue) pos = InsertPosition.AtIndex(item.Index.Value);
                else if (!string.IsNullOrEmpty(item.After)) pos = InsertPosition.AfterElement(item.After);
                else if (!string.IsNullOrEmpty(item.Before)) pos = InsertPosition.BeforeElement(item.Before);

                if (!string.IsNullOrEmpty(item.From))
                {
                    var resultPath = handler.CopyFrom(item.From, parentPath, pos);
                    return $"Copied to {resultPath}";
                }
                else
                {
                    var type = item.Type ?? "";
                    // Wrap props in a tracking dict (matches CLI/resident add): a
                    // key the handler reads is consumed, so UnusedKeys after Add
                    // is the generic unsupported-prop set across ALL handlers.
                    // Previously batch/MCP add saw only Word's curated
                    // LastAddUnsupportedProps, silently dropping an unknown prop

View on GitHub (pinned to 1ced45e900)