huggingface/transformers · error · HTTPException

'input' must be a string or list

Error message

'input' must be a string or list

What it means

In the Responses-style API path, ResponseHandler reads body['input'] and accepts exactly two shapes: a plain string (treated as a single user message) or a list (either flat content items, or OpenAI Responses-style dicts with a 'role'). Any other JSON type — number, object, boolean, null — triggers HTTP 422 "'input' must be a string or list".

Source

Thrown at src/transformers/cli/serving/response.py:498

            - **Multi-turn list** — messages and tool call items (``function_call``,
              ``function_call_output``) from a previous response, converted via
              :meth:`_normalize_response_items`.

        If ``instructions`` is present, it is prepended as a system message.
        """
        inp = body["input"]
        instructions = body.get("instructions")

        if isinstance(inp, str):
            messages = [{"role": "user", "content": inp}]
        elif isinstance(inp, list):
            if inp and "role" not in inp[0]:
                # Flat content list (single-turn, e.g. input_text/input_image)
                messages = [{"role": "user", "content": inp}]
            else:
                messages = ResponseHandler._normalize_response_items(inp)
        else:
            raise HTTPException(status_code=422, detail="'input' must be a string or list")

        # Prepend instructions as a system message
        if instructions:
            if messages and messages[0]["role"] == "system":
                messages[0]["content"] = instructions
            else:
                messages.insert(0, {"role": "system", "content": instructions})

        return messages

    @staticmethod
    def _normalize_response_items(items: list[dict]) -> list[dict]:
        """Convert a list of Responses API items into chat messages.

        Input items may be a mix of:
            - Messages (``EasyInputMessageParam`` with ``role``, or ``type: "message"``).
            - ``reasoning`` — buffered and attached as ``reasoning_content`` to the next assistant message.
            - ``function_call`` — merged as ``tool_calls`` onto the preceding assistant message.

View on GitHub (pinned to a597f97485)

Solutions

  1. Send input as a string: {"input": "Hello"}
  2. Or as a list: {"input": [{"type": "input_text", "text": "Hello"}]} or a list of role dicts
  3. Wrap object payloads into a single-item content list before sending

Example fix

# before
curl -X POST .../responses -d '{"model": "gpt2", "input": {"text": "hi"}}'  # 422

# after
curl -X POST .../responses -d '{"model": "gpt2", "input": "hi"}'
Defensive patterns

Strategy: type-guard

Validate before calling

inp = body.get("input")
if not isinstance(inp, (str, list)):
    return JSONResponse(status_code=422, content={"error": "'input' must be a string or list"})

Type guard

def is_valid_input(inp) -> bool:
    return isinstance(inp, (str, list))

Try / catch

resp = await client.post(url, json=body)
if resp.status_code == 422 and "must be a string or list" in resp.text:
    body["input"] = str(body["input"])
    resp = await client.post(url, json=body)

Prevention

When it happens

Trigger: POSTing {"input": 123} or {"input": {"text": "hi"}}; sending null when no input; a client SDK serializing the field to a non-array/object primitive; malformed JSON that decodes 'input' to a scalar.

Common situations: Migrating from OpenAI Responses API with an object-form input; missing the input field so a default scalar is injected; hand-rolled curl payloads with wrong types.

Related errors


AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14). Data as JSON: /api/errors/2ddfaebde792f46b. Report an issue: GitHub.