openai/openai-python · error · TypeError
Raw multipart alternative must be a string
Error message
Raw multipart alternative must be a string
What it means
When a multipart request consists solely of the raw-body alternative field, the SDK sends it verbatim as the entire request body, which requires a string it can UTF-8 encode. Any other type (int, dict, bytes) cannot be sent as the raw alternative and raises TypeError.
Source
Thrown at src/openai/_multipart.py:31
extra_body: Body | None,
encodings: Mapping[str, tuple[str, bool]],
raw_body_field: str | None = None,
existing_files: RequestFiles | None = None,
) -> tuple[dict[str, object] | None, RequestFiles | None, bytes | None, str]:
"""Prepare explicitly encoded form fields without flattening their JSON contents."""
if not isinstance(body, Mapping):
raise TypeError("Multipart request body must be a mapping")
if extra_body is not None and not isinstance(extra_body, Mapping):
raise TypeError("Multipart extra_body must be a mapping")
original = cast(Mapping[str, object], body)
overrides = cast(Mapping[str, object], extra_body or {})
merged = {key: value for key, value in {**original, **overrides}.items() if not isinstance(value, (Omit, NotGiven))}
# A raw request alternative is safe only when there is no other payload to lose.
# Explicit null remains a JSON part; only an omitted field selects the raw body.
if not existing_files and raw_body_field is not None and set(merged) == {raw_body_field}:
value = merged[raw_body_field]
if not isinstance(value, str):
raise TypeError("Raw multipart alternative must be a string")
return None, None, value.encode("utf-8"), encodings[raw_body_field][0]
files: list[tuple[str, FileTypes]] = list(
existing_files.items() if isinstance(existing_files, Mapping) else (existing_files or [])
)
for name, (content_type, as_json) in encodings.items():
if name not in merged:
continue
value = merged.pop(name)
if as_json:
data = openapi_dumps(value)
else:
if not isinstance(value, str):
raise TypeError(f"Multipart field {name!r} must be a string")
data = value.encode("utf-8")
files.append((name, (None, data, content_type)))
return merged or None, files, None, "multipart/form-data"
View on GitHub (pinned to 9917c6e28e)
Solutions
- Serialize the raw body to a string first (e.g. json.dumps(...) or the required text format) before passing it
- If you intend normal multipart encoding, ensure other fields are present so the raw-alternative path isn't selected
- Double-check you're populating the designated raw-body field with text
Example fix
// before
client.resources.create(raw='{"a": 1}') # passing a dict
client.resources.create(raw={'a': 1})
// after
client.resources.create(raw=json.dumps({'a': 1})) Defensive patterns
Strategy: type-guard
Validate before calling
if raw_body is not None and not isinstance(raw_body, str):
raw_body = json.dumps(raw_body) Type guard
from typing import TypeGuard
def is_raw_alternative(value: object) -> TypeGuard[str]:
return isinstance(value, str) Prevention
- Serialize raw-body alternatives to a string yourself before calling the API
- Don't put structured data into the raw-body field
When it happens
Trigger: A multipart call where every other field is NotGiven/Omit and the raw_body_field value is not a str — e.g. passing a dict or pre-encoded bytes as the raw alternative.
Common situations: Using the raw-alternative escape hatch (e.g. sending a pre-rendered form or template string) but passing structured data instead of its serialized string form.
Related errors
- Multipart field {name!r} must be a string
- Multipart request body must be a mapping
- Multipart extra_body must be a mapping
- Expected custom parse type to be a subclass of {Stream} or {
- Pagination is only supported with mappings
AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28).
Data as JSON: /api/errors/13655f58c2d17023.
Report an issue: GitHub.