iOfficeAI/OfficeCLI · error · OfficeCliError

${r.stderr || r.stdout}

Error message

${r.stderr || r.stdout}

What it means

Thrown by Document._start() (officecli open) when the resident-startup spawn returns a non-zero exit. The message is the CLI's own stderr (or stdout), so it carries officecli's reason verbatim. _start only spawns when no live resident already serves the file (serves() is a real liveness probe), so this is a genuine failure to bring the resident up.

Source

Thrown at sdk/node/index.js:464

  constructor(filePath, binary = 'officecli', timeoutMs = 30000) {
    // Canonical (Windows 8.3-expanded) so the pipe name AND the serves() path
    // comparison both match what the resident reports.
    this.path = canonicalPath(filePath);
    this.bin = resolveBinary(binary);
    this.timeout = timeoutMs; // connect timeout (ms); the reply read blocks
    const [main, ping] = pipePaths(this.path);
    this._main = main;
    this._ping = ping;
    this._restarting = null; // in-flight dead-resident restart (serializes callers)
  }

  async _start() {
    // Reuse a resident already serving this file (no spawn). serves() is a real
    // liveness probe (ping + path match), so a stale/dead socket falls through
    // to `officecli open`, which replaces it via TryConnect.
    if (await serves(this._ping, this.path)) return;
    const r = runCli(this.bin, ['open', this.path]);
    if (r.status !== 0) throw new OfficeCliError(r.status == null ? -1 : r.status, r.stderr || r.stdout);
  }

  async _cmd(command, args, props, asJson = true, timeoutMs) {
    const req = { Command: command, Json: asJson };
    if (args) req.Args = strMap(args);
    if (props !== null && props !== undefined) req.Props = strMap(props);
    const t = timeoutMs == null ? this.timeout : timeoutMs;
    try {
      return await rpc(this._main, req, t, BUSY_MAX_RETRIES);
    } catch (e) {
      if (!(e instanceof OfficeCliError)) throw e;
      // Delivery failed. Use the -ping pipe to tell DEAD from BUSY:
      //   • ALIVE but main pipe unresponsive → do NOT bypass it (a second writer
      //     racing the live resident loses data on its save). Re-raise.
      //   • DEAD (crashed / stale socket) → restart with one `officecli open`
      //     and retry ONCE. Safe across reads and mutations.
      if (await this.alive()) throw e;
      // Serialize the restart across concurrent callers sharing this Document so

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Read the embedded stderr — it states officecli's exact reason (e.g. file_locked, corrupt, unsupported format).
  2. For a lock: close the other holder (Excel or another officecli resident) then retry, or call oc.open after releasing it.
  3. For a corrupt/unrecognized file: open the correct source file or recreate it.
  4. If a stale lock persists, remove the lock file (officecli's lock location, per the error) and retry.

Example fix

// before: oc.open('report.xlsx') -> [exit N] <officecli stderr: file_locked>
// after: release the other holder, then open
await otherDoc.close();
const doc = await oc.open('report.xlsx');
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the file isn't locked by another holder before open()
const fs = require('fs');
function writable(filePath) {
  try { const fd = fs.openSync(filePath, 'r+'); fs.closeSync(fd); return true; }
  catch { return false; } // absent or locked
}

Try / catch

// Surface officecli's own reason, then recover by releasing the holder
try { await oc.open(filePath); }
catch (e) {
  if (/file_locked|lock/i.test(e.message)) { await releaseHolder(); await oc.open(filePath); }
  else throw e;
}

Prevention

When it happens

Trigger: The target file is locked by another process/instance (file_locked); the file is corrupt or an unrecognized format; the path is invalid/unwritable; officecli open rejects the document for a format-specific reason; a stale lockfile blocks open.

Common situations: The same document is open in Excel/another officecli resident; a previous resident crashed leaving a lock; opening a non-Office file by mistake; read-only directory.

Related errors


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