{"record":{"id":"75f535931ad55feb","repo":"hiyouga/LlamaFactory","slug":"invalid-input-type-input-item-type","errorCode":null,"errorMessage":"Invalid input type {input_item.type}.","messagePattern":"Invalid input type (.+?)\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"src/llamafactory/api/chat.py","lineNumber":161,"sourceCode":"                        check_ssrf_url(video_url)\n                        video_stream = requests.get(video_url, stream=True).raw\n\n                    videos.append(video_stream)\n                elif input_item.type == \"audio_url\":\n                    text_content += AUDIO_PLACEHOLDER\n                    audio_url = input_item.audio_url.url\n                    if re.match(r\"^data:audio\\/(mpeg|mp3|wav|ogg);base64,(.+)$\", audio_url):  # base64 audio\n                        audio_stream = io.BytesIO(base64.b64decode(audio_url.split(\",\", maxsplit=1)[1]))\n                    elif os.path.isfile(audio_url):  # local file\n                        check_lfi_path(audio_url)\n                        audio_stream = audio_url\n                    else:  # web uri\n                        check_ssrf_url(audio_url)\n                        audio_stream = requests.get(audio_url, stream=True).raw\n\n                    audios.append(audio_stream)\n                else:\n                    raise HTTPException(\n                        status_code=status.HTTP_400_BAD_REQUEST, detail=f\"Invalid input type {input_item.type}.\"\n                    )\n\n            input_messages.append({\"role\": ROLE_MAPPING[message.role], \"content\": text_content})\n        else:\n            input_messages.append({\"role\": ROLE_MAPPING[message.role], \"content\": message.content})\n\n    tool_list = request.tools\n    if isinstance(tool_list, list) and len(tool_list):\n        try:\n            tools = json.dumps([dictify(tool.function) for tool in tool_list], ensure_ascii=False)\n        except json.JSONDecodeError:\n            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=\"Invalid tools\")\n    else:\n        tools = None\n\n    return input_messages, system, tools, images or None, videos or None, audios or None\n","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/api/chat.py#L143-L179","documentation":"Raised as HTTP 400 during request preprocessing when a message's content is a list and one of its items has a type other than the supported multimodal types (text, image_url, video_url, audio_url). The endpoint iterates content parts and only handles those four types; anything else (e.g. 'file', 'input_audio', or a typo) is rejected.","triggerScenarios":"POST /v1/chat/completions with content: [{type: 'file_url', ...}] or [{type: 'text', text: 'hi'}, {type: 'emoji', ...}]; sending OpenAI's newer 'input_audio' type instead of 'audio_url'; a client SDK that emits an unsupported content-part schema.","commonSituations":"Upgrading a client library that changed content-part type names; copying payloads from OpenAI docs that include types LlamaFactory does not implement; hand-built JSON payloads with a typo in the type field.","solutions":["Remove or replace unsupported content parts; only 'text', 'image_url', 'video_url', 'audio_url' are accepted.","If you intended audio, use {type: 'audio_url', audio_url: {url: ...}} rather than 'input_audio'.","Add a client-side filter over message.content lists to drop non-supported types before posting.","Check this repo's chat.py content-part loop for the canonical accepted shapes if unsure."],"exampleFix":"// before\ncontent: [\n  {type: 'text', text: 'describe this'},\n  {type: 'input_audio', input_audio: {data: '...', format: 'wav'}}\n]\n// after\ncontent: [\n  {type: 'text', text: 'describe this'},\n  {type: 'audio_url', audio_url: {url: 'data:audio/wav;base64,...'}}\n]","handlingStrategy":"validation","validationCode":"ALLOWED = {\"text\", \"image_url\", \"video_url\", \"audio_url\"}\ndef valid_content_parts(messages):\n    for m in messages:\n        if isinstance(m.get(\"content\"), list):\n            for part in m[\"content\"]:\n                if part.get(\"type\") not in ALLOWED:\n                    return False\n    return True","typeGuard":"const ALLOWED = new Set(['text', 'image_url', 'video_url', 'audio_url']);\nconst hasValidContentParts = (msgs) => msgs.every(m =>\n  typeof m.content === 'string' ||\n  (Array.isArray(m.content) && m.content.every(p => ALLOWED.has(p.type)))\n);","tryCatchPattern":"catch (e) { if (e.status === 400 && e.detail?.startsWith('Invalid input type')) { const bad = /* find part not in ALLOWED */; body = stripPart(body, bad); retry once; } else throw e; }","preventionTips":["Centralize content-part construction in one helper that only emits the four supported types.","Map OpenAI's input_audio to audio_url before sending to LlamaFactory.","Pin client SDK versions so content-part schemas do not drift silently."],"tags":["api","multimodal","validation","http-400","chat"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}