How to Handle Structured Error Codes like `not_found` in OfficeCLI

OfficeCLI distinguishes transport failures (raised as OfficeCliError) from business failures (returned as structured JSON envelopes with machine-readable error codes like not_found), allowing precise, programmatic error handling in both Python and CLI workflows.

OfficeCLI provides a robust error-handling architecture that separates connectivity issues from application-level failures. Understanding how to work with structured error codes like not_found is essential for building resilient automation scripts and AI agents that interact with Office documents programmatically. This guide covers the two-layer error model and demonstrates practical handling patterns in Python, Bash, and Node.js according to the iOfficeAI/OfficeCLI source code.

Understanding OfficeCLI's Two-Layer Error Model

OfficeCLI operates with a clear separation between transport and business error layers. This design prevents fragile string parsing and enables reliable programmatic responses.

Layer Cause Surface Behavior
Transport / pipe errors Cannot reach resident (missing binary, busy pipe, connection timeout) Raises OfficeCliError with numeric code
Business / command errors Valid connection, but operation fails (missing slide, invalid cell reference) Returns JSON envelope with success: false and structured error object containing string code

The not_found code is one of several canonical error codes defined by OfficeCLI. Others include invalid_value, unsupported_property, and type-specific variants. These machine-readable codes enable automatic recovery flows—for example, catching not_found and using the accompanying suggestion field to list valid alternatives.

Handling not_found in the Python SDK

The Python SDK implements this two-layer model in sdk/python/officecli.py. Transport errors raise OfficeCliError, while business errors remain in the response envelope for inspection.

Pattern: Safe Element Retrieval with Structured Error Handling

import officecli
from officecli import OfficeCliError

def safe_get(doc_path, element_path):
    """Return the element data or `None` if it does not exist."""
    try:
        doc = officecli.open(doc_path)               # auto-install + resident start

        resp = doc.send({"command": "get", "path": element_path})
    except OfficeCliError as e:                      # transport-level problem

        raise RuntimeError(f"Pipe failure [{e.code}]: {e}") from e

    # Business-level response – always a dict (JSON envelope)

    if isinstance(resp, dict) and not resp.get("success", True):
        err = resp["error"]
        code = err.get("code")
        if code == "not_found":
            # Structured handling for "not found"

            print(f"⚠️  {err['error']} – suggested fix: {err.get('suggestion')}")
            return None
        # Handle other structured codes as needed

        raise RuntimeError(f"OfficeCLI error [{code}]: {err['error']}")
    return resp

Key implementation details from sdk/python/officecli.py (lines 91-104):

  • OfficeCliError carries numeric error codes for transport failures
  • The _parse method processes JSON envelopes without raising on business errors
  • The error object contains code (string), error (human message), and optional suggestion

Pattern: Batch Operations with Per-Item Error Inspection

items = [
    {"command": "set", "path": "/slide[1]/shape[5]", "props": {"text": "Hi"}},
    {"command": "remove", "path": "/slide[10]"}
]
doc = officecli.open('deck.pptx')
result = doc.batch(items, stop_on_error=False)

for r in result:
    if isinstance(r, dict) and not r.get('success', True):
        print('Item error', r['error']['code'], r['error']['error'])
        if r['error']['code'] == 'not_found':
            print('  Suggestion:', r['error'].get('suggestion'))

Setting stop_on_error=False allows the batch to continue, returning individual error envelopes for failed items while completing successful ones.

Handling not_found in CLI Workflows

The OfficeCLI binary returns business errors as JSON when using the --json flag, enabling reliable parsing with tools like jq.

Detecting Structured Error Codes in Bash


# Try to get a slide that does not exist

officecli get deck.pptx /slide[99] --json

# → {"success":false,"error":{"error":"Slide 99 not found (total: 8)","code":"not_found","suggestion":"Valid Slide index range: 1-8"}}

Exit codes indicate transport status only. A zero exit code with success: false in the JSON means the command delivered successfully but the operation failed at the business layer.

Programmatic Error Handling with jq

out=$(officecli get deck.pptx /slide[99] --json)

if code=$(jq -r '.error.code // empty' <<<"$out"); then
    if [[ $code == "not_found" ]]; then
        echo "Slide missing – $(jq -r '.error.suggestion' <<<"$out")"
        # Could automatically list available slides here

    else
        echo "Other OfficeCLI error: $code"
    fi
else
    echo "Success or transport failure (check exit code $?)"
fi

Handling not_found in the Node.js SDK

The Node SDK follows the same architectural pattern, as declared in sdk/node/index.d.ts (lines 40-46). Business errors return in the envelope; transport errors throw.

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

(async () => {
  const doc = await oc.open('deck.pptx');
  try {
    const resp = await doc.send({command: 'get', path: '/slide[99]'});
    if (!resp.success) {
      if (resp.error.code === 'not_found') {
        console.warn('Slide missing →', resp.error.suggestion);
        // Implement recovery: list valid indices and retry
      }
    }
  } catch (e) {
    // Transport-level failure: numeric e.code
    console.error('Transport error', e.code, e.message);
  }
})();

Key Source Files and Implementation Details

File Purpose Relevant Lines
sdk/python/officecli.py OfficeCliError class, pipe transport, JSON envelope parsing (_parse) 91-104
README.md Error format specification, canonical error code list Error section
sdk/node/index.d.ts TypeScript declarations for OfficeCliError and response envelopes 40-46

Common Error Codes and Recovery Strategies

  • not_found: Requested element does not exist. Check suggestion for valid ranges or use parent enumeration commands.
  • invalid_value: Type or format mismatch. The suggestion often indicates expected format.
  • unsupported_property: Property exists on similar objects but not this specific type. Use get to inspect available properties.

Summary

  • Transport errors raise OfficeCliError with numeric codes—catch these with try/except or .catch()
  • Business errors return as JSON envelopes with string code fields like not_found—inspect resp.success and resp.error.code
  • The suggestion field provides actionable recovery hints without additional API calls
  • Use --json in CLI workflows and stop_on_error=False in batch operations for granular error handling
  • Reference sdk/python/officecli.py, sdk/node/index.d.ts, and the README error format section for implementation details

Frequently Asked Questions

What is the difference between OfficeCliError and the error object in the response envelope?

OfficeCliError indicates a transport failure—the SDK could not communicate with the OfficeCLI resident. It carries a numeric code and is raised as an exception. The error object in the response envelope indicates a business failure—the command was delivered but the operation itself failed. It contains a string code like not_found and remains in the returned dictionary for inspection.

How do I distinguish not_found from other error codes programmatically?

Check resp["error"]["code"] (Python) or resp.error.code (Node.js) against the string "not_found". The comparison is case-sensitive. Other canonical codes include invalid_value, unsupported_property, and type_mismatch. The README error format section documents the complete list.

Can I suppress not_found errors and return a default value instead?

Yes. Wrap the send() call in a helper function that returns None or a default when code == "not_found". The Python safe_get() example demonstrates this pattern. For CLI workflows, use jq to filter and provide defaults: jq '. // {"default": true}'.

What should I do with the suggestion field?

The suggestion field contains a human-readable remediation hint—often a valid range or alternative path. Log it for users, use it to automatically adjust parameters and retry, or present it in interactive UIs. It is optional; always use .get("suggestion") or ?? "" to handle its absence.

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 →