How OfficeCLI Ensures Data Integrity During Batch Operations
OfficeCLI guarantees data integrity during batch operations through deferred document saving, atomic all-or-nothing execution by default, and comprehensive per-item error tracking that prevents partial updates from persisting to disk.
The iOfficeAI/OfficeCLI repository provides a robust command-line interface and .NET SDK for automating Microsoft Office documents. When executing multiple commands via JSON batches, the tool implements a protective pipeline to ensure data integrity, prevent document corruption, and avoid expensive re-serialization operations.
Deferred Saving and Single Write Semantics
OfficeCLI prevents the O(N²) re-serialization problem through a deferred saving mechanism. Before processing begins, the RunNonResidentBatch method sets the handler's DeferSave flag to true in src/officecli/CommandBuilder.Batch.cs (lines 14-15).
This flag instructs the underlying document handler to accumulate all changes in memory rather than writing to disk after each individual command. The document is serialized and written exactly once after all batch items complete successfully, eliminating the risk of partial writes leaving the file in an inconsistent state.
Atomic Execution and Transaction Safety
By default, batch operations are atomic—they follow an all-or-nothing semantics. The batchStopOpt logic in src/officecli/CommandBuilder.Batch.cs (lines 30-38) ensures that if any single command fails, the entire batch is rolled back and the exit code indicates failure, leaving the original document untouched.
You can modify this behavior using command-line flags:
--best-effort– Switches to a partial-apply mode where successful commands persist and failures are reported individually.--stop-on-error– Forces immediate termination at the first failure, handled by thestopOnErrorflag logic (lines 58-77), preventing subsequent commands from executing on potentially corrupted state.
Granular Error Handling and Per-Item Capture
Each command in a batch is wrapped in a try / catch block within the ApplyBatchItems loop (src/officecli/CommandBuilder.Batch.cs, lines 55-78). For every item processed, OfficeCLI generates a BatchResult object that records:
- The success or failure status
- Output values
- Specific error codes
This granular tracking allows callers to inspect exactly which items failed without guessing, while the deferred save mechanism ensures those failures never reach the document on disk.
Additionally, the pipeline captures unrecognized LaTeX diagnostics after each item (lines 78-96). Any LaTeX warnings emitted by the underlying handler are collected and returned to the caller, preventing silent loss of warning information that might indicate data integrity issues.
Conflict Prevention in Resident Mode
When operating with a resident server that keeps documents open in memory, OfficeCLI prevents file handle conflicts through the skipResidentOnlyCommands check (src/officecli/CommandBuilder.Batch.cs, lines 59-66). This logic identifies commands such as open and close that would attempt to re-open or close an already-managed document handle, silently skipping them to prevent access violations and corruption.
The resident mode components in src/officecli/ResidentServer.cs and src/officecli/ResidentClient.cs coordinate with the batch engine to maintain consistent state without redundant file operations.
Strict Input Validation
Before any document mutation occurs, the BatchItemConverter class in src/officecli/BatchTypes.cs (lines 67-99) validates the incoming JSON payload. This converter tolerates both modern object-style and legacy array-style ["key=value"] property formats, rejecting malformed inputs early with clear error messages.
By enforcing a strict schema on the BatchItem data model, OfficeCLI ensures that corrupted or malformed commands never reach the execution stage, protecting the document from invalid mutations.
Practical Implementation Examples
Running an Atomic Batch via CLI
Create a JSON batch file and execute it with default atomic semantics:
cat > batch.json <<'EOF'
[
{"command":"add","parent":"/slide[1]","type":"shape","props":{"text":"Hello","x":"1cm","y":"2cm"}},
{"command":"set","path":"/slide[1]/shape[1]","props":{"bold":"true"}},
{"command":"remove","path":"/slide[2]/shape[3]"}
]
EOF
officecli batch myPresentation.pptx --input batch.json
If any command fails, the whole batch aborts and myPresentation.pptx remains unchanged.
Best-Effort Mode for Partial Application
To apply successful commands while logging failures:
officecli batch myPresentation.pptx --input batch.json --best-effort
Strict Stop-on-Error Mode
To halt immediately at the first error:
officecli batch myPresentation.pptx --input batch.json --stop-on-error
Programmatic Batch Execution with the .NET SDK
using OfficeCli;
using System.Collections.Generic;
var items = new List<BatchItem>
{
new BatchItem { Command = "add", Parent = "/slide[1]", Type = "shape",
Props = new Dictionary<string, string>{ {"text","Hello"}, {"x","1cm"}, {"y","2cm"} } },
new BatchItem { Command = "set", Path = "/slide[1]/shape[1]",
Props = new Dictionary<string, string>{ {"bold","true"} } },
new BatchItem { Command = "remove", Path = "/slide[2]/shape[3]" }
};
var doc = await OfficeCli.SDK.open("myPresentation.pptx");
// Execute with atomic semantics (StopOnError = false maintains atomicity; true creates stop-on-error behavior)
var results = await doc.batch(items, new BatchOptions { StopOnError = false, Force = false });
foreach (var r in results)
Console.WriteLine($"{r.Index}: {(r.Success ? "OK" : "FAIL")} – {r.Output}");
The SDK respects the same DeferSave and atomic logic as the CLI, ensuring data integrity whether used interactively or programmatically.
Summary
- Deferred saving writes the document only once after all commands complete, preventing partial updates and O(N²) serialization costs.
- Atomic-by-default execution ensures all commands succeed or none persist, with
--best-effortand--stop-on-errorproviding alternative strategies. - Per-item error capture via
BatchResultobjects provides granular failure diagnostics without corrupting the document. - Resident mode safeguards skip conflicting
openandclosecommands to prevent file handle corruption. - Strict JSON validation through
BatchItemConverterrejects malformed payloads before they reach the document handlers.
Frequently Asked Questions
What happens if one command in a batch fails by default?
By default, OfficeCLI uses atomic execution. If any command throws an exception, the entire batch is rolled back according to the logic in src/officecli/CommandBuilder.Batch.cs, the document remains unchanged on disk, and the CLI returns a failure exit code.
How does deferred saving improve both performance and integrity?
The DeferSave flag accumulates mutations in memory and performs a single write operation at the end of the batch. This eliminates the risk of intermediate save states corrupting the file if a later command fails, while also avoiding the performance penalty of re-serializing the entire document after every individual command.
Can I process batch commands while a document is open in resident mode?
Yes. When a resident server holds the document open, the batch engine automatically skips open and close commands via the skipResidentOnlyCommands check (lines 59-66), preventing file handle conflicts while allowing other mutations to proceed safely against the in-memory document.
What is the difference between --best-effort and --stop-on-error?
--best-effort switches the batch from atomic to partial-apply mode, executing all commands that can succeed and reporting failures for those that cannot. --stop-on-error maintains atomic semantics but aborts immediately upon the first failure, ensuring no subsequent commands execute on potentially invalid state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →