How to Use OfficeCLI Batch Mode for Atomic Multi-Command Execution with Rollback

OfficeCLI batch mode executes multiple commands as a single atomic transaction—if any command fails, the resident process automatically rolls back all changes, leaving the document unchanged.

OfficeCLI's batch mode lets you send a list of batch-item objects to the resident in one round-trip, guaranteeing atomicity by default. This guide explains how to structure batch payloads, control rollback behavior, and handle errors using both the Node.js SDK and the raw CLI. All examples reference the actual implementation in iOfficeAI/OfficeCLI.

Understanding Atomic Batch Execution

According to the OfficeCLI source code, the batch command is described as "atomic by default—any failed item rolls back the whole batch" in the README at line 17【https://github.com/iOfficeAI/OfficeCLI/blob/main/README.md#batch-mode---multi-command-execution-atomic-by-default-any-failed-item-rolls-back-the-whole-batch‑line‑17-L17-L31】. This means you can safely chain dependent operations without worrying about partial state corruption.

The atomic guarantee is enforced by the resident process, not the SDK. The Node SDK's Document.batch() method at lines 519-522 simply forwards the JSON-encoded list to the resident via the pipe protocol【https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.js#L519-L522】.

Batch-Item Structure

Each item in your batch array must contain these keys (see lines 500-505)【https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.js#L500-L505】:

Key Required Description
command (or op) Yes The operation to perform: set, get, delete, etc.
path Yes Document path targeting a cell, shape, or property
props Yes Object containing operation-specific parameters

Additional keys are forwarded verbatim as arguments to the underlying command.

Using the Node.js SDK for Atomic Batches

The SDK provides the cleanest interface for OfficeCLI batch mode with rollback guarantees. Here's the complete pattern:

const oc = require('@officecli/sdk');

// Open or create a document in resident mode
const doc = await oc.open('financial-report.xlsx');

// Build your batch as an array of operation objects
const batch = [
  {
    command: 'set',
    path: '/Sheet1/A1',
    props: { text: 'Q1 Revenue' }
  },
  {
    command: 'set',
    path: '/Sheet1/B1',
    props: { formula: '=SUM(C2:C10)' }
  },
  {
    command: 'format',
    path: '/Sheet1/A1:B1',
    props: { bold: true, bgColor: '#4472C4' }
  }
];

// Execute atomically—any failure triggers automatic rollback
try {
  const result = await doc.batch(batch);
  console.log('All commands applied:', result);
} catch (err) {
  // Document remains unchanged if any item failed
  console.error('Batch failed, rollback complete:', err);
}

On delivery failure, the SDK throws OfficeCliError (defined at the top of sdk/node/index.js). Successful batches return a parsed JSON envelope with operation results.

Control Flags for Batch Behavior

The SDK accepts options that map directly to CLI flags (lines 519-522)【https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.js#L519-L522】:

Flag SDK Option Default Behavior
--force force: true true Execute even if resident reports busy status
--stop-on-error stopOnError: true false Halt processing on first failure (still rolls back entire batch)
--best-effort bestEffort: true false Disable atomic rollback—keep successful items, skip failures

Force Mode

When force: true (default), the resident queues your batch even during active operations. Set force: false to fail fast if the resident is busy:

// Fail immediately if resident cannot accept the batch
await doc.batch(criticalUpdates, { force: false });

Stop-on-Error Behavior

By default, the resident processes all items to identify every failure. Enable stopOnError to abort early—note this does not disable rollback:

// Stop at first failure (still atomic—everything rolls back)
await doc.batch(largeBatch, { stopOnError: true });

Disabling Rollback: Best-Effort Mode

For non-critical operations where partial success is acceptable, use best-effort mode. This opt-in behavior disables the atomic guarantee.

Via CLI:

officecli batch report.pptx --input updates.json --best-effort --json

Via SDK:

// Apply what succeeds, skip what fails—no rollback
const partial = await doc.batch(
  [setTitle, setSubtitle, riskyOperation],
  { bestEffort: true }
);

// partial.results contains per-item success/failure status

Raw CLI Usage for Atomic Batches

You can invoke batch mode directly without the SDK, receiving identical atomic guarantees:


# Pipe JSON array to the batch command

echo '[
  {"command":"set","path":"/Slide1/Title","props":{"text":"Q3 Report"}},
  {"command":"set","path":"/Slide1/Subtitle","props":{"text":"Draft"}},
  {"command":"add","path":"/Slide1","props":{"type":"chart","left":100,"top":200}}
]' | officecli batch presentation.pptx --json

The resident parses the stdin JSON, validates each item, and executes as a transaction. Output is a JSON envelope:

{
  "success": true,
  "transactionId": "txn_7f3a9b",
  "results": [
    {"status": "ok", "path": "/Slide1/Title"},
    {"status": "ok", "path": "/Slide1/Subtitle"},
    {"status": "ok", "path": "/Slide1/Shape3", "id": "Shape3"}
  ]
}

Error Handling Patterns

The SDK distinguishes between transport failures (throws OfficeCliError) and command failures (returned in the result envelope). Structure your error handling accordingly:

const { OfficeCliError } = require('@officecli/sdk');

try {
  const result = await doc.batch(operations);
  
  // Check for command-level failures even on "success"
  const failures = result.results.filter(r => r.status !== 'ok');
  if (failures.length > 0) {
    console.warn('Some commands failed (batch was rolled back):', failures);
  }
} catch (transportError) {
  if (transportError instanceof OfficeCliError) {
    // Pipe broken, resident crashed, or timeout
    console.error('Cannot reach resident:', transportError.code);
  }
}

Key Implementation Files

File Relevance
sdk/node/index.js Document.batch() implementation, lines 500-522【https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.js】
README.md Batch command specification and flag documentation【https://github.com/iOfficeAI/OfficeCLI/blob/main/README.md#batch-mode】
ResidentServer.cs / ResidentClient.cs C# resident process handling actual transaction semantics and rollback

| Wiki: command-batch | Extended syntax reference: https://github.com/iOfficeAI/OfficeCLI/wiki/command-batch |

Summary

  • Atomic by default: OfficeCLI batch mode treats your command list as a transaction—any failure triggers automatic rollback inside the resident process.
  • SDK is a thin wrapper: The Node SDK's Document.batch() forwards JSON to the resident without adding logic; guarantees come from the resident itself.
  • Structure items correctly: Each batch item needs command/op, path, and props keys.
  • Control with flags: Use force, stopOnError, and bestEffort to adjust behavior—only bestEffort disables rollback.
  • Handle two error types: Catch OfficeCliError for transport problems; inspect result envelopes for command failures.

Frequently Asked Questions

What happens if one command in a batch fails?

The resident automatically rolls back all changes from that batch, leaving the document in its pre-batch state. This atomic guarantee is the default behavior in OfficeCLI batch mode and requires no additional code from you.

Can I disable rollback and keep partial results?

Yes. Pass --best-effort to the CLI or { bestEffort: true } to the SDK. This opt-in mode applies successful commands and skips failures without rolling back. Use it only when partial state is acceptable.

How do I know if a batch succeeded or failed?

The SDK returns a parsed JSON envelope on success—inspect result.success and result.results[]. On transport failure (broken pipe, crashed resident), the SDK throws OfficeCliError. The CLI exits with non-zero status and prints error details to stderr.

Does the SDK add any transaction logic?

No. The Node SDK at sdk/node/index.js lines 519-522 merely encodes your batch array and writes it to the resident's pipe. All transaction semantics, validation, and rollback logic live in the resident process—making the SDK lightweight and the guarantees consistent across all clients.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →