{"record":{"id":"0d18d1699e8a8336","repo":"iOfficeAI/OfficeCLI","slug":"send-empty-item","errorCode":null,"errorMessage":"send: empty item","messagePattern":"send: empty item","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/BatchExecutor.cs","lineNumber":86,"sourceCode":"\n    /// <summary>\n    /// Mirrors the SDKs' <c>send(item)</c> (as distinct from <c>batch(items)</c>):\n    /// runs ONE batch-shaped item and returns EXACTLY what the CLI single command\n    /// writes to stdout — with <paramref name=\"json\"/> the standalone command's\n    /// envelope (WrapEnvelopeText's `{success,data,message}` for text commands\n    /// like set/add/remove, WrapEnvelope's `{success,data}` for data commands like\n    /// get/query), without it the plain text. A failure throws instead of being\n    /// captured per-item, matching the standalone command's exit contract.\n    /// </summary>\n    /// <param name=\"itemJson\">A single batch item object — the same shape as one\n    /// entry in <see cref=\"ExecuteBatch\"/>'s array, or the SDKs' `send(item)` argument.</param>\n    /// <param name=\"json\">Mirrors the CLI `--json` toggle: structured envelope vs plain text.</param>\n    public static string ExecuteSend(IDocumentHandler handler, string itemJson, bool json)\n    {\n        try\n        {\n            var item = JsonSerializer.Deserialize(itemJson, BatchJsonContext.Default.BatchItem)\n                ?? throw new ArgumentException(\"send: empty item\");\n            var inner = CommandBuilder.ExecuteBatchItem(handler, item, json);\n            if (!json) return inner;\n\n            // Match the standalone command's envelope by inner shape — a JSON payload\n            // (get/query/view/validate/…) wraps with WrapEnvelope; a plain-text\n            // message (set/add/remove/…) wraps with WrapEnvelopeText (which adds the\n            // `message` field the CLI emits). Content-based so a new command inherits\n            // the right envelope without a hand-kept command→envelope map.\n            var trimmed = inner.TrimStart();\n            return trimmed.StartsWith('{') || trimmed.StartsWith('[')\n                ? OutputFormatter.WrapEnvelope(inner)\n                : OutputFormatter.WrapEnvelopeText(inner);\n        }\n        catch (Exception ex)\n        {\n            return RenderTopLevelError(ex, json);\n        }\n    }","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/BatchExecutor.cs#L68-L104","documentation":"ExecuteSend deserializes a single batch-item JSON string into a BatchItem object. If deserialization returns null (the JSON was empty, whitespace-only, or a JSON null literal), the coalescing ?? operator throws ArgumentException. This mirrors the SDK send(item) contract: a null/empty item is a caller bug, not a batch-level failure. Unlike ExecuteBatch, the failure throws (not captured per-item) to match the standalone command's exit contract.","triggerScenarios":"Calling BatchExecutor.ExecuteSend with an empty string, \"null\", whitespace, or a JSON literal that deserializes to null. Common in SDK send(item) wrappers that forward user-supplied input without checking for emptiness.","commonSituations":"An SDK wrapper passes a default/empty string when the user provides no item. A serialization bug produces 'null' or '' instead of a valid item JSON. A network layer sends an empty body after stripping content.","solutions":["Ensure itemJson is a valid BatchItem JSON object before calling ExecuteSend — check for null/empty/whitespace.","Guard at the caller boundary: if (string.IsNullOrWhiteSpace(itemJson)) throw with a descriptive message.","Verify the JSON is not the literal \"null\" — JsonSerialializer.Deserialize returns null for JSON null."],"exampleFix":"// before\nvar result = BatchExecutor.ExecuteSend(handler, itemJson, json);\n// after\nif (string.IsNullOrWhiteSpace(itemJson) || itemJson.Trim() == \"null\")\n    throw new ArgumentException(\"itemJson must be a non-empty batch item JSON object.\");\nvar result = BatchExecutor.ExecuteSend(handler, itemJson, json);","handlingStrategy":"validation","validationCode":"static void ValidateItemJson(string itemJson)\n{\n    if (string.IsNullOrWhiteSpace(itemJson))\n        throw new ArgumentException(\"itemJson must be a non-empty JSON object.\");\n    var trimmed = itemJson.Trim();\n    if (trimmed == \"null\")\n        throw new ArgumentException(\"itemJson is JSON null — provide a batch item object.\");\n}\n\n// Usage:\nValidateItemJson(itemJson);\nvar result = BatchExecutor.ExecuteSend(handler, itemJson, json);","typeGuard":null,"tryCatchPattern":"try { var result = BatchExecutor.ExecuteSend(handler, itemJson, json); }\ncatch (ArgumentException ex) when (ex.Message == \"send: empty item\")\n{\n    // The caller passed null/empty/whitespace/\"null\" JSON\n    throw new ArgumentException(\"Batch item was empty. Provide a valid JSON item object.\", ex);\n}","preventionTips":["Always check string.IsNullOrWhiteSpace before calling ExecuteSend.","Guard against the JSON literal \"null\" which deserializes to null.","In SDK wrappers, validate user-supplied input before forwarding to ExecuteSend."],"tags":["batch","send","json-deserialization","empty-input"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}