openai/openai-python · error · TypeError

Multipart field {name!r} must be a string

Error message

Multipart field {name!r} must be a string

What it means

Fields listed in the multipart encodings map with as_json=False must be plain strings so they can be UTF-8 encoded as form parts. If such a field holds a non-string (int, dict, list, None), encoding is impossible and TypeError is raised naming the field.

Source

Thrown at src/openai/_multipart.py:45

    # 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

  1. Convert the value to a string before the call (str(value), or json.dumps for structured data if the field accepts JSON text)
  2. Move structured data into a field designed for JSON encoding or into extra_body
  3. Check the endpoint's parameter type and match it

Example fix

// before
client.foo.create(text_field={"a": 1})
# or
client.foo.create(text_field=42)
// after
client.foo.create(text_field=json.dumps({"a": 1}))
# or
client.foo.create(text_field="42")
Defensive patterns

Strategy: type-guard

Validate before calling

payload = {k: (v if isinstance(v, str) or k in JSON_FIELDS else json.dumps(v)) for k, v in payload.items()}

Type guard

from typing import TypeGuard

def all_text_fields_str(fields: dict[str, object], text_fields: set[str]) -> TypeGuard[dict[str, str]]:
    return all(isinstance(fields.get(k), str) for k in text_fields)

Prevention

When it happens

Trigger: A multipart request where a text-encoded field (declared string in the API schema) receives a non-string value — e.g. passing metadata as a dict to a field the SDK encodes as text, or an int where a string enum is expected.

Common situations: Passing numbers/booleans/dicts to string-typed multipart parameters; forgetting that only as_json fields accept structured values.

Related errors


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