Skip to content

Add __reduce__ method to Exception types #2629

Open

Description

@deMarigny-ZEFR

Confirm this is a feature request for the Python library and not the underlying OpenAI API.

  • This is a feature request for the Python library

Describe the feature or improvement you're requesting

When trying to use the openai-python library in an environment that requires pickling, like Ray, OpenAI exceptions cause failures like:

7m    �[39;49;00m�[33m"traceback: Traceback (most recent call last):"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"  File \"/opt/conda/lib/python3.10/site-packages/ray/exceptions.py\", line 51, in from_ray_exception"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"    return pickle.loads(ray_exception.serialized_exception)"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"  File \"/opt/conda/lib/python3.10/site-packages/openai/_exceptions.py\", line 101, in __init__"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"    super().__init__(message=\"Request timed out.\", request=request)"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"TypeError: super(type, obj): obj must be an instance or subtype of type"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m""�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"The above exception was the direct cause of the following exception:"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m""�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"Traceback (most recent call last):"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"  File \"/opt/conda/lib/python3.10/site-packages/ray/_private/serialization.py\", line 460, in deserialize_objects"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"    obj = self._deserialize_object(data, metadata, object_ref)"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"  File \"/opt/conda/lib/python3.10/site-packages/ray/_private/serialization.py\", line 342, in _deserialize_object"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"    return RayError.from_bytes(obj)"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"  File \"/opt/conda/lib/python3.10/site-packages/ray/exceptions.py\", line 45, in from_bytes"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"    return RayError.from_ray_exception(ray_exception)"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"  File \"/opt/conda/lib/python3.10/site-packages/ray/exceptions.py\", line 54, in from_ray_exception"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"    raise RuntimeError(msg) from e"�[39;49;00m,�[37m�[39;49;00m
�[37m    �[39;49;00m�[33m"RuntimeError: Failed to unpickle serialized exception"�[39;49;00m,�[37m�[39;49;00m

This is because there is no __reduce__ method defined on the openai exceptions.

See Ray documentation on serialization here: https://docs.ray.io/en/latest/ray-core/objects/serialization.html#customized-serialization, im sure this would affect other runtime environments too

Can a __reduce__ method be added to exceptions so runtimes, like Ray, do not fail when there is an OpenAI exception?

Additional context

No response

Activity

  1. changed the title [-]Add __reduce__ method to Exception types[/-] [+]Add ``__reduce__`` method to Exception types[/+] on Sep 12, 2025
  2. dajiaohuang commented on Sep 25, 2026

    @dajiaohuang

    Proposed Fix

    Here is a concrete implementation approach. The core issue is that exceptions containing httpx2.Request and httpx2.Response objects (which may not be safely picklable across processes) need custom __reduce__ methods to work with distributed frameworks like Ray.

    Solution: Add __reduce__ methods that serialize only essential data

    For src/openai/_exceptions.py:

    1. Base OpenAIError already works fine (it inherits from Exception)

    2. For APIError (parent of most exceptions):

    def __reduce__(self):
        return (
            _reconstruct_api_error,
            (
                type(self),
                self.message,
                self.code,
                self.param,
                self.type,
                {
                    "method": str(self.request.method) if hasattr(self, "request") else None,
                    "url": str(self.request.url) if hasattr(self, "request") else None,
                    "headers": dict(self.request.headers) if hasattr(self, "request") else None,
                },
                self.body,
            ),
        )
    1. Helper reconstruction function at module level:
    def _reconstruct_api_error(cls, message, code, param, error_type, request_info, body):
        err = cls.__new__(cls)
        Exception.__init__(err, message)
        err.message = message
        err.code = code
        err.param = param
        err.type = error_type
        err.body = body
        err._request_info = request_info
        return err
    1. For APIStatusError and subclasses, also include status_code and request_id in the serialized state.

    2. Special cases:

    • SubjectTokenProviderError, LengthFinishReasonError, ContentFilterFinishReasonError, WebSocketConnectionClosedError need similar __reduce__ methods appropriate to their fields
    • InvalidWebhookSignatureError(ValueError) should work by default since it has no custom init args

    The key insight from the error trace is that during unpickling, super() is being called incorrectly because the class reference is not properly restored. Using __reduce__ with a module-level reconstruction function solves this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions