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

> Learn to handle structured error codes like not_found in OfficeCLI. Separate business and transport errors for precise programmatic handling in Python and CLI.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: how-to-guide
- Published: 2026-08-03

---

**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](https://github.com/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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

```python
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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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

```python
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

```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`

```bash
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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.d.ts) (lines 40-46). Business errors return in the envelope; transport errors throw.

```javascript
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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/python/officecli.py) | `OfficeCliError` class, pipe transport, JSON envelope parsing (`_parse`) | [91-104](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/python/officecli.py#L91-L104) |
| [`README.md`](https://github.com/iOfficeAI/OfficeCLI/blob/main/README.md) | Error format specification, canonical error code list | [Error section](https://github.com/iOfficeAI/OfficeCLI/blob/main/README.md#56) |
| [`sdk/node/index.d.ts`](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.d.ts) | TypeScript declarations for `OfficeCliError` and response envelopes | [40-46](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.d.ts#L40-L46) |

## 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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/python/officecli.py), [`sdk/node/index.d.ts`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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.