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

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 manifest that points to an external OpenAPI specification. According to the Sentry plugin configuration in 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, 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 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 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, 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 and the NGS analysis scripts:

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:

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:

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 manifest.
  • Log internally, sanitize externally: Write full tracebacks to files like runner_exception.txt as shown in 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 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) 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 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 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), 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.

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 →