# How to Handle Errors in an OpenAI Plugin: Complete Implementation Guide

> Learn how to handle errors in an OpenAI plugin with a complete implementation guide. Discover standardized JSON error payloads and best practices from the openai/plugins repository.

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

---

**OpenAI plugins handle errors by wrapping skill logic in `try-except` blocks and returning a standardized JSON payload with `"ok": false`, a numeric error code, and a safe user-facing message, as implemented in the `openai/plugins` repository.**

OpenAI plugins are structured as collections of independent skills that require consistent error handling to communicate failures to the ChatGPT model. The `openai/plugins` repository establishes a strict error-handling contract that every skill must follow to ensure predictable, secure error responses. This guide explains the implementation pattern using actual source files from the repository.

## The OpenAI Plugin Error-Handling Contract

Every skill in the repository follows a consistent contract to ensure the runtime can reliably parse success and failure states.

### Standardized JSON Response Structure

On failure, skills must return a JSON object with an `"ok": false` field and an `"error"` object containing a numeric code and human-readable message. According to the UKB-TOPMed PheWAS skill documentation in [`plugins/life-science-research/skills/ukb-topmed-phewas-skill/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/life-science-research/skills/ukb-topmed-phewas-skill/SKILL.md), the contract requires:

```json
{
  "ok": false,
  "error": {
    "code": 400,
    "message": "Human-readable description",
    "details": {}
  }
}

```

The `"details"` field is optional and should be omitted for privacy-critical errors.

### Error Code Mapping and Security

Plugins map exceptions to stable HTTP-style error codes: `400` for bad input, `404` for not found, `500` for internal failures, and `502` for upstream service errors. The error message must never include stack traces, API keys, or internal tokens. As implemented in [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py), sensitive information is filtered out to comply with OpenAI security policies while exceptions are logged internally for operator observability.

## The [`_error.py`](https://github.com/openai/plugins/blob/main/_error.py) Utility Implementation

The repository provides a shared utility to standardize error construction across all skills.

### Centralized Error Builder

The `build_error_response` helper function is located at `plugins/<plugin-name>/skills/<skill-name>/scripts/_error.py`. This utility is explicitly defined in [`plugins/zotero/skills/zotero/scripts/_error.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/_error.py) and [`plugins/nvidia/skills/omniverse-cad-to-simready/shared/_error.py`](https://github.com/openai/plugins/blob/main/plugins/nvidia/skills/omniverse-cad-to-simready/shared/_error.py):

```python
def build_error_response(exc: Exception, code: int = 500) -> dict:
    """Return a JSON-serialisable error payload."""
    # Never expose internal traceback – only the message.

    return {
        "ok": False,
        "error": {
            "code": code,
            "message": str(exc),
        },
    }

```

### Integration in Skill Scripts

Each skill imports this helper and wraps its main logic in a `try-except` block. The Zotero skill in [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py) demonstrates this pattern:

```python
from _error import build_error_response
import logging

def fetch_item(item_id: str):
    try:
        # ... network call to Zotero ...

        return {"ok": True, "item": data}
    except requests.HTTPError as exc:
        logging.exception("Zotero API failure")
        return build_error_response(exc, code=502)
    except Exception as exc:
        logging.exception("Unexpected Zotero error")
        return build_error_response(exc)

```

## Real-World Error Handling Examples

The repository contains multiple implementations demonstrating how to handle different failure modes.

### Life Science Research Skills

The UKB-TOPMed PheWAS skill in [`plugins/life-science-research/skills/ukb-topmed-phewas-skill/scripts/ukb_topmed_phewas.py`](https://github.com/openai/plugins/blob/main/plugins/life-science-research/skills/ukb-topmed-phewas-skill/scripts/ukb_topmed_phewas.py) maps specific exceptions to appropriate status codes:

```python
import json
from _error import build_error_response

def main():
    try:
        # Parse input, call remote API, gather results ...

        result = {"association": [...]}
        return {"ok": True, "data": result}
    except ValueError as exc:               # e.g. malformed request

        return build_error_response(exc, code=400)
    except ConnectionError as exc:          # e.g. external service down

        return build_error_response(exc, code=502)
    except Exception as exc:                # unexpected bug

        return build_error_response(exc)    # defaults to 500

```

### NVIDIA Omniverse Skills

The NVIDIA "Omniverse CAD → SimReady" skill uses the shared utility at [`plugins/nvidia/skills/omniverse-cad-to-simready/shared/_error.py`](https://github.com/openai/plugins/blob/main/plugins/nvidia/skills/omniverse-cad-to-simready/shared/_error.py). The file [`plugins/nvidia/skills/omniverse-cad-to-simready/shared/simready_package_check_dependencies.py`](https://github.com/openai/plugins/blob/main/plugins/nvidia/skills/omniverse-cad-to-simready/shared/simready_package_check_dependencies.py) demonstrates converting raised exceptions into structured error responses while maintaining debuggability through internal logging.

## Summary

- **Return consistent shapes**: Always return `{"ok": false, "error": {...}}` for failures and `{"ok": true, ...}` for successes.
- **Use the helper**: Import `build_error_response` from [`_error.py`](https://github.com/openai/plugins/blob/main/_error.py) to ensure consistent payload structure.
- **Map codes appropriately**: Use `400` for client errors, `404` for missing resources, `502` for upstream failures, and `500` for internal errors.
- **Secure by default**: Never expose stack traces, API keys, or tokens in error messages; log these internally using `logging.exception()`.
- **Document in SKILL.md**: Each skill defines its error contract in its [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file, such as [`plugins/zotero/skills/zotero/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/SKILL.md).

## Frequently Asked Questions

### What JSON structure should error responses follow in OpenAI plugins?

Error responses must return a JSON object with `"ok": false` and an `"error"` object containing `"code"` (integer), `"message"` (string), and optionally `"details"` (dictionary). This contract is documented in skill definition files like [`plugins/life-science-research/skills/ukb-topmed-phewas-skill/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/life-science-research/skills/ukb-topmed-phewas-skill/SKILL.md) and enforced by the `build_error_response` utility.

### Where is the error handling utility located in the openai/plugins repository?

The shared helper is typically located at `plugins/<plugin-name>/skills/<skill-name>/scripts/_error.py`. Specific implementations exist in [`plugins/zotero/skills/zotero/scripts/_error.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/_error.py) and [`plugins/nvidia/skills/omniverse-cad-to-simready/shared/_error.py`](https://github.com/openai/plugins/blob/main/plugins/nvidia/skills/omniverse-cad-to-simready/shared/_error.py), which are imported by individual skill scripts to standardize error formatting.

### How do OpenAI plugins prevent sensitive data leaks in error messages?

Plugins use the `build_error_response` function to ensure only the exception message is returned, omitting stack traces and secret values. Internal details are logged using `logging.exception()` for operator visibility, while the JSON response contains only user-safe text, as required by the OpenAI security policies implemented in the plugin runtime.

### What error codes should OpenAI plugin skills return?

Use standard HTTP-style codes: `400` for client input errors, `404` for missing resources, `502` for upstream service failures (like external API outages), and `500` for unexpected internal errors. The Zotero skill in [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py) demonstrates mapping specific exceptions like `requests.HTTPError` to code `502`.