BerriAI/litellm · error · HTTPException

400

400

Error message

Expected 1 model, got {len(target_model_names)}

What it means

LiteLLM 'unified' file ids embed routing metadata (base64 of litellm_proxy:<purpose>;unified_id,...;target_model_names,<comma-separated models>). When POST /v1/batches receives such an id as input_file_id, the proxy parses target_model_names and must pick exactly one model to create the batch against; with a different count it returns HTTP 400 'Expected 1 model, got N'.

Source

Thrown at litellm/proxy/batches_endpoints/endpoints.py:282

                )

            response.input_file_id = input_file_id

        elif litellm.enable_loadbalancing_on_batch_endpoints is True and is_router_model and router_model is not None:
            if llm_router is None:
                raise HTTPException(
                    status_code=500,
                    detail={"error": "LLM Router not initialized. Ensure models added to proxy."},
                )

            response = await llm_router.acreate_batch(**_create_batch_data)
        elif (
            unified_file_id and input_file_id
        ):  # litellm_proxy:application/octet-stream;unified_id,c4843482-b176-4901-8292-7523fd0f2c6e;target_model_names,gpt-4o-mini
            target_model_names: Final = get_models_from_unified_file_id(unified_file_id)
            ## EXPECTS 1 MODEL
            if len(target_model_names) != 1:
                raise HTTPException(
                    status_code=400,
                    detail={"error": f"Expected 1 model, got {len(target_model_names)}"},
                )
            model: Final = target_model_names[0]
            _create_batch_data["model"] = model

            resolved_storage_url: Final = await _resolve_managed_input_file_storage_url(input_file_id)
            if resolved_storage_url is not None:
                _create_batch_data["input_file_id"] = resolved_storage_url

            if llm_router is None:
                raise HTTPException(
                    status_code=500,
                    detail={"error": "LLM Router not initialized. Ensure models added to proxy."},
                )

            _create_batch_data.update(disable_fallbacks=True)  # pyright: ignore[reportCallIssue]  # router flag
            response = await llm_router.acreate_batch(**_create_batch_data)

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Re-upload the input file scoped to exactly one target model and use that unified file id in the batch request.
  2. Create per-model file copies and one batch per model when several providers must run the same input.
  3. For load-balanced batching, send a router model name in the batch body with enable_loadbalancing_on_batch_endpoints instead of a multi-model file id.

Example fix

# before - file uploaded for two models, then used for a batch
curl -X POST "$PROXY/v1/files" -H "Authorization: Bearer $KEY" \
  -F purpose=batch -F file=@input.jsonl -F 'target_model_names=gpt-4o-mini,gemini-2.0-flash'
curl -X POST "$PROXY/v1/batches" -H "Authorization: Bearer $KEY" \
  -d '{"input_file_id": "<multi-model-unified-file-id>"}'
# after - one target model per file
curl -X POST "$PROXY/v1/files" -H "Authorization: Bearer $KEY" \
  -F purpose=batch -F file=@input.jsonl -F 'target_model_names=gpt-4o-mini'
curl -X POST "$PROXY/v1/batches" -H "Authorization: Bearer $KEY" \
  -d '{"input_file_id": "<single-model-file-id>"}'
Defensive patterns

Strategy: validation

Validate before calling

import base64

def count_target_models(unified_file_id: str) -> int:
    try:
        raw = base64.b64decode(unified_file_id + '==').decode('utf-8', errors='ignore')
    except Exception:
        return 0
    for part in raw.split(';'):
        if part.startswith('target_model_names,'):
            return len([m for m in part.split(',', 1)[1].split(',') if m.strip()])
    return 0

# run before creating a batch
assert count_target_models(input_file_id) == 1, 'use a single-model unified file id'

Type guard

import base64

def is_single_model_file_id(file_id: str) -> bool:
    try:
        raw = base64.b64decode(file_id + '==').decode('utf-8', errors='ignore')
    except Exception:
        return False
    for part in raw.split(';'):
        if part.startswith('target_model_names,'):
            names = [m for m in part.split(',', 1)[1].split(',') if m.strip()]
            return len(names) == 1
    return False

Try / catch

try:
    batch = await client.batches.create(input_file_id=fid, ...)
except openai.BadRequestError as e:
    if 'Expected 1 model' in str(e):
        fid = await reupload_scoped_to_single_model()  # re-upload with one target model
        batch = await client.batches.create(input_file_id=fid, ...)
    else:
        raise

Prevention

When it happens

Trigger: POST /v1/batches where input_file_id is a unified file id whose target_model_names lists 2+ models (file uploaded for multiple models) or encodes an empty model list.

Common situations: Reusing a multi-model upload (meant for unified file retrieval across providers) as batch input; copying file ids from fan-out workflow logs; hand-built batch payloads referencing shared files.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/62bc109b2ed06ef6. Report an issue: GitHub.