{"record":{"id":"66f935b349f53980","repo":"iOfficeAI/OfficeCLI","slug":"file-required","errorCode":"file_required","errorMessage":"No document file specified — the file path is required. In MCP/batch, pass `file` as the top-level argument (it applies to every command); do not put `file` inside individual batch commands.","messagePattern":"No document file specified — the file path is required\\. In MCP/batch, pass `file` as the top-level argument \\(it applies to every command\\); do not put `file` inside individual batch commands\\.","errorType":"exception","errorClass":"CliException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/DocumentHandlerFactory.cs","lineNumber":24,"sourceCode":"using System.Text.RegularExpressions;\nusing OfficeCli.Core;\nusing OfficeCli.Core.Plugins;\n\nnamespace OfficeCli.Handlers;\n\npublic static class DocumentHandlerFactory\n{\n    public static IDocumentHandler Open(string filePath, bool editable = false)\n    {\n        // An empty/whitespace path otherwise falls through to File.Exists →\n        // \"File not found: \" with a blank tail, which actively misleads: the\n        // caller can't tell the file is *missing as an argument* from *present\n        // but wrong*. The single most common way to hit this is an MCP/batch\n        // call that omits the top-level `file` (e.g. a model that replicates the\n        // single-command shape and puts `file` inside each batch item instead).\n        // Give one clear, project-wide message at the shared open chokepoint.\n        if (string.IsNullOrWhiteSpace(filePath))\n            throw new CliException(\"No document file specified — the file path is required. \"\n                + \"In MCP/batch, pass `file` as the top-level argument (it applies to every command); \"\n                + \"do not put `file` inside individual batch commands.\")\n            {\n                Code = \"file_required\",\n                Suggestion = \"Provide the document path as the top-level file argument.\"\n            };\n\n        if (!File.Exists(filePath))\n            throw new CliException($\"File not found: {filePath}\")\n            {\n                Code = \"file_not_found\",\n                Suggestion = \"Check the file path. Use an absolute path or a path relative to the current directory.\",\n                Help = \"officecli create <path> --type docx|xlsx|pptx\"\n            };\n\n        // CONSISTENCY(corrupt-file-rejection): a 0-byte file is silently\n        // accepted by Open XML SDK 3.x in read-write mode (it materialises an\n        // empty Package), but the resulting handler returns a fake root node","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/DocumentHandlerFactory.cs#L6-L42","documentation":"Thrown by DocumentHandlerFactory.Open when filePath is null, empty, or whitespace. This is the shared chokepoint for every CLI command and MCP/batch invocation — it catches the case where the top-level 'file' argument was omitted entirely, which would otherwise fall through to File.Exists and produce a misleading 'File not found: ' message with a blank tail. The error message specifically addresses the most common MCP/batch misuse: putting 'file' inside individual batch command items instead of as the top-level argument.","triggerScenarios":"Calling Open(null), Open(\"\"), or Open(\"   \"). In MCP/batch mode, this happens when the caller structures the request with 'file' nested inside each command item (e.g. {\"commands\": [{\"op\": \"add\", \"file\": \"doc.docx\", ...}]}) rather than at the top level ({\"file\": \"doc.docx\", \"commands\": [...]}). In CLI mode, running a command that requires a file without specifying the --file flag.","commonSituations":"An LLM model replicating the single-command shape and nesting 'file' inside batch items; a script that conditionally sets the file variable but hits a code path where it stays null; a CI pipeline passing an empty environment variable expansion (e.g. $FILE_PATH where the env var is unset).","solutions":["In MCP/batch: pass 'file' as the top-level key in the request, not inside each command: {\"file\": \"path.docx\", \"commands\": [{...}]}","In CLI: ensure the file path argument is provided before the subcommand arguments.","Check for unset environment variables: use a default or fail with a clear message before calling the CLI.","If calling programmatically, assert filePath is non-empty before invoking Open."],"exampleFix":"// before (MCP batch — file nested inside commands, top-level file is absent)\n{\n  \"commands\": [\n    { \"op\": \"add\", \"path\": \"/p1\", \"file\": \"report.docx\", \"text\": \"Hello\" }\n  ]\n}\n\n// after (file at top level, applies to all commands)\n{\n  \"file\": \"report.docx\",\n  \"commands\": [\n    { \"op\": \"add\", \"path\": \"/p1\", \"text\": \"Hello\" }\n  ]\n}","handlingStrategy":"validation","validationCode":"// Validate before calling Open\nif (string.IsNullOrWhiteSpace(filePath))\n    throw new ArgumentException(\"File path is required.\");\nvar handler = DocumentHandlerFactory.Open(filePath);","typeGuard":"static bool IsValidFilePath(string? path) => !string.IsNullOrWhiteSpace(path);","tryCatchPattern":"try\n{\n    var handler = DocumentHandlerFactory.Open(filePath);\n}\ncatch (CliException ex) when (ex.Code == \"file_required\")\n{\n    // The file argument was missing — for MCP/batch, move 'file' to the top level\n    logger.LogError(\"Missing file argument. In MCP/batch, pass file at the top level, not inside each command.\");\n    throw;\n}","preventionTips":["Always validate filePath is non-null and non-whitespace before calling Open.","In MCP/batch requests, structure the payload with 'file' at the top level: {\"file\": \"...\", \"commands\": [...]}.","When scripting, use parameter expansion with a fallback or error: filePath = env.FILE ?? throw.","Add a schema validation step for incoming MCP/batch JSON to catch missing top-level keys."],"tags":["file-handling","mcp-batch","argument-validation","file-required"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}