# How to Describe Error Handling in an OpenAI Plugin's API: A Complete Technical Guide

> Learn to describe error handling in your OpenAI plugin API using structured JSON and HTTP status codes. Ensure safe, actionable errors for ChatGPT. Explore the openai/plugins repository.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-07-12

---

**OpenAI plugins communicate failures through structured JSON error objects containing `error`, `code`, and `status` fields, using standard HTTP status codes defined in the OpenAPI specification to ensure the ChatGPT runtime can surface safe, actionable messages without exposing internal implementation details.**

The `openai/plugins` repository demonstrates that robust error handling is contractual rather than optional. Each plugin describes its failure modes through an OpenAPI specification, while skill implementations wrap external API calls in defensive `try/except` blocks to translate exceptions into machine-readable payloads. This approach prevents raw stack traces from reaching the LLM while preserving diagnostic information for developers.

## The OpenAPI Error Contract

### Defining HTTP Status Codes and Response Schemas

Every plugin declares its API surface via a [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) manifest that points to an external OpenAPI specification. According to the Sentry plugin configuration in [`plugins/sentry/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/sentry/.codex-plugin/plugin.json), the `api.url` field references a schema that must define standard HTTP status codes: **4xx** for client errors such as invalid parameters or authentication failures, and **5xx** for server-side failures. The OpenAPI schema must declare a consistent error response object structure that the ChatGPT runtime can reliably parse.

### Standard Error Payload Structure

The runtime expects a uniform JSON shape when operations fail. The payload must include:

- **`error`**: A human-readable string describing the failure
- **`code`**: A machine-parseable error identifier (e.g., `TIMEOUT`, `HTTP_ERROR`)
- **`status`**: The HTTP status code integer
- **`details`**: Optional additional context for complex failure scenarios

This structure ensures the ChatGPT client can display helpful messages without executing or exposing arbitrary error content to the conversation context.

## Implementing Error Handlers in Plugin Skills

### Wrapping External API Calls

Skill scripts must isolate third-party API failures to prevent cascading errors. In [`plugins/nvidia/skills/omniverse-cad-to-simready/references/preflight/scripts/preflight.py`](https://github.com/openai/plugins/blob/main/plugins/nvidia/skills/omniverse-cad-to-simready/references/preflight/scripts/preflight.py), the implementation wraps external operations in `try/except` blocks to catch network timeouts and validation errors before they propagate. When an external service returns a non-2xx response or raises a connection error, the script constructs a JSON payload with the standardized fields rather than allowing the raw exception to surface to the LLM.

### Exception Logging and Safe Failures

The [`plugins/ngs-analysis/scripts/run_scrnaseq_fastq_to_count.py`](https://github.com/openai/plugins/blob/main/plugins/ngs-analysis/scripts/run_scrnaseq_fastq_to_count.py) file demonstrates the critical pattern of logging detailed tracebacks to persistent storage while returning generic error messages to the runtime. When an unexpected exception occurs, the script writes the full traceback to [`runner_exception.txt`](https://github.com/openai/plugins/blob/main/runner_exception.txt) on disk, then returns a sanitized payload to the user. This prevents internal implementation details from leaking into the LLM context while maintaining debuggability for plugin developers.

### Explicit Exception Status Flags

Some plugins use an explicit **`status: "exception"`** field in their response payloads to signal the runtime that the operation encountered a fatal error rather than returning partial data. This pattern appears in NVIDIA's pre-flight checks within [`preflight.py`](https://github.com/openai/plugins/blob/main/preflight.py), where the script must distinguish between successful data retrieval and workflow-blocking errors that require immediate user intervention.

## Practical Error Handling Examples

### Python: Defensive Wrapper with Logging

Based on patterns from [`preflight.py`](https://github.com/openai/plugins/blob/main/preflight.py) and the NGS analysis scripts:

```python
import json
import requests
from typing import Dict, Any

def call_external_api(endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]:
    """
    Execute an external API call with standardized error translation.
    """
    try:
        response = requests.post(endpoint, json=params, timeout=30)
        response.raise_for_status()
        return response.json()
    except requests.HTTPError as http_err:
        # Translate HTTP errors into plugin-compatible format

        return {
            "error": f"External service error: {response.status_code}",
            "code": "EXTERNAL_API_ERROR",
            "status": response.status_code
        }
    except Exception as exc:
        # Log full traceback for debugging, return safe message

        with open("logs/runner_exception.txt", "a") as log_file:
            log_file.write(f"{type(exc).__name__}: {exc}\n")
        
        return {
            "error": "Internal plugin error occurred. Check logs for details.",
            "code": "INTERNAL_ERROR",
            "status": 500,
            "status_flag": "exception"  # Explicit error signal

        }

```

### JavaScript: Node.js Fetch Implementation

For Node.js-based plugins, implement similar defensive patterns:

```javascript
import fetch from 'node-fetch';

/**
 * Execute plugin endpoint with uniform error handling.
 */
export async function executeSkill(url, payload) {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      const errorText = await response.text();
      return {
        error: `Plugin request failed: ${errorText}`,
        code: 'HTTP_ERROR',
        status: response.status
      };
    }
    
    return await response.json();
  } catch (networkError) {
    return {
      error: `Network failure: ${networkError.message}`,
      code: 'NETWORK_ERROR',
      status: 502
    };
  }
}

```

### OpenAPI Specification Snippet

Define error responses in your OpenAPI document to satisfy the contract:

```yaml
paths:
  /analyzeData:
    post:
      summary: Analyze external data source
      responses:
        '200':
          description: Successful analysis
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: string
        '4XX':
          description: Client error
          content:
            application/json:
              schema:
                type: object
                required: [error, code, status]
                properties:
                  error:
                    type: string
                    description: Human-readable error message
                  code:
                    type: string
                    description: Machine-readable error code
                  status:
                    type: integer
                    description: HTTP status code
        '5XX':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

```

## Summary

- **Standardize on JSON**: Always return error objects containing `error`, `code`, and `status` fields to maintain compatibility with the ChatGPT runtime.
- **Use OpenAPI contracts**: Define all 4xx and 5xx responses in the OpenAPI specification referenced by your [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) manifest.
- **Log internally, sanitize externally**: Write full tracebacks to files like [`runner_exception.txt`](https://github.com/openai/plugins/blob/main/runner_exception.txt) as shown in [`plugins/ngs-analysis/scripts/run_scrnaseq_fastq_to_count.py`](https://github.com/openai/plugins/blob/main/plugins/ngs-analysis/scripts/run_scrnaseq_fastq_to_count.py), but return generic messages to the API consumer.
- **Wrap external calls**: Isolate third-party failures using `try/except` blocks that translate exceptions into the standardized payload format.
- **Signal explicitly**: Use `status: "exception"` flags where appropriate to distinguish between partial data and fatal errors.

## Frequently Asked Questions

### What HTTP status codes should an OpenAI plugin return for errors?

Return **4xx** status codes for client-side issues such as invalid parameters or missing authentication, and **5xx** codes for server-side failures or upstream service outages. The OpenAPI specification referenced in your [`plugin.json`](https://github.com/openai/plugins/blob/main/plugin.json) must document these response codes and their corresponding JSON schemas to ensure the runtime can parse them correctly.

### How do I prevent sensitive error details from leaking to the ChatGPT user?

Wrap all operations in `try/except` blocks and write full exception tracebacks to internal log files (e.g., [`runner_exception.txt`](https://github.com/openai/plugins/blob/main/runner_exception.txt)) rather than returning them in the API response. Return only sanitized JSON payloads containing a generic `error` message, a machine-readable `code`, and the HTTP `status`. This pattern appears in [`plugins/ngs-analysis/scripts/run_scrnaseq_fastq_to_count.py`](https://github.com/openai/plugins/blob/main/plugins/ngs-analysis/scripts/run_scrnaseq_fastq_to_count.py) within the repository.

### Should I use custom exception classes in my plugin code?

Yes, defining custom exception classes improves code organization and enables precise error translation. The [`plugins/mixpanel-headless/skills/mixpanelyst/scripts/help.py`](https://github.com/openai/plugins/blob/main/plugins/mixpanel-headless/skills/mixpanelyst/scripts/help.py) file demonstrates enumerating custom exception types to document expected failure modes. However, these should be caught and converted to the standard JSON error format before being returned through the API.

### How does the ChatGPT runtime know when a plugin function failed?

The runtime inspects the HTTP status code and the response body structure. If the response contains an `error` field or a `status: "exception"` flag (as implemented in [`plugins/nvidia/skills/omniverse-cad-to-simready/references/preflight/scripts/preflight.py`](https://github.com/openai/plugins/blob/main/plugins/nvidia/skills/omniverse-cad-to-simready/references/preflight/scripts/preflight.py)), the runtime interprets this as a failure and surfaces the contained message to the user rather than attempting to process the payload as successful data.