openai/openai-python · error · TypeError

invalid type; expected {native_expected_type}, string, bytes

Error message

invalid type; expected {native_expected_type}, string, bytes, int or float

What it means

The SDK's datetime/date coercion helper accepts str, bytes, int, or float and calls float(value) to convert. When the value's type is neither convertible (TypeError from float(), e.g. None, list, dict, or an arbitrary object) the helper raises this TypeError to signal that the input type is fundamentally unconvertible. It is raised from within parse_datetime/parse_date when coercing a raw value, usually because the caller passed a None or a non-scalar where a datetime-like value was expected.

Source

Thrown at src/openai/_utils/_datetime_parse.py:41


EPOCH = datetime(1970, 1, 1)
# if greater than this, the number is in ms, if less than or equal it's in seconds
# (in seconds this is 11th October 2603, in ms it's 20th August 1970)
MS_WATERSHED = int(2e10)
# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
MAX_NUMBER = int(3e20)


def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]:
    if isinstance(value, (int, float)):
        return value
    try:
        return float(value)
    except ValueError:
        return None
    except TypeError:
        raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None


def _from_unix_seconds(seconds: Union[int, float]) -> datetime:
    if seconds > MAX_NUMBER:
        return datetime.max
    elif seconds < -MAX_NUMBER:
        return datetime.min

    while abs(seconds) > MS_WATERSHED:
        seconds /= 1000
    dt = EPOCH + timedelta(seconds=seconds)
    return dt.replace(tzinfo=timezone.utc)


def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]:
    if value == "Z":
        return timezone.utc
    elif value is not None:

View on GitHub (pinned to 9917c6e28e)

Solutions

  1. Ensure the field is a str/bytes/int/float (e.g. an ISO-8601 string or unix seconds) before assignment
  2. Fix the upstream data so the timestamp field is never None or a container
  3. If the value may be absent, use a conditional/exclude the field rather than passing None
  4. Catch TypeError at the boundary that produces the raw data and log which field is malformed

Example fix

# before
obj = SomeModel(created=None)

# after
obj = SomeModel(created=int(time.time()))
# or omit the field if the model allows
Defensive patterns

Strategy: validation

Validate before calling

if not isinstance(value, (str, bytes, int, float)):
    raise ValueError(f"bad timestamp: {value!r}")

Type guard

def is_coercible_ts(v: object) -> bool:
    return isinstance(v, (str, bytes, int, float)) and not isinstance(v, bool)

Try / catch

try:
    parse_datetime(raw)
except TypeError as e:
    raise ValueError(f"field X must be str/int/float, got {type(raw)}") from e

Prevention

When it happens

Trigger: Passing None, a dict, list, datetime object already parsed, or any non-string/number object to a field the SDK model coerces into a datetime or date (e.g. constructing model objects locally from raw dicts, or datetime parsing of untyped payloads via parse_datetime/parse_date).

Common situations: Building request/response models manually from JSON where a timestamp field is null; copying pydantic-v1 style code that passed datetime fields as dicts; upgrading code that previously silently passed through bad values before coercion was added.

Related errors


AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28). Data as JSON: /api/errors/c8ebe5a647e05867. Report an issue: GitHub.