{"record":{"id":"fc3ccd8c50f53db6","repo":"oobabooga/textgen","slug":"role-function-is-not-supported","errorCode":null,"errorMessage":"role: function is not supported.","messagePattern":"role: function is not supported\\.","errorType":"exception","errorClass":"InvalidRequestError","httpStatus":400,"severity":"error","filePath":"modules/api/completions.py","lineNumber":500,"sourceCode":"        raise InvalidRequestError(message=\"function_call is not supported.\", param='function_call')\n\n    if 'messages' not in body:\n        raise InvalidRequestError(message=\"messages is required\", param='messages')\n\n    tools = None\n    if 'tools' in body and body['tools'] is not None and isinstance(body['tools'], list) and body['tools']:\n        tools = validateTools(body['tools'])  # raises InvalidRequestError if validation fails\n\n    tool_choice = body.get('tool_choice', None)\n    if tool_choice == \"none\":\n        tools = None  # Disable tool detection entirely\n\n    messages = body['messages']\n    for m in messages:\n        if 'role' not in m:\n            raise InvalidRequestError(message=\"messages: missing role\", param='messages')\n        elif m['role'] == 'function':\n            raise InvalidRequestError(message=\"role: function is not supported.\", param='messages')\n\n        # Handle multimodal content validation\n        content = m.get('content')\n        if content is None:\n            # OpenAI allows content: null on assistant messages when tool_calls is present\n            if m['role'] == 'assistant' and m.get('tool_calls'):\n                m['content'] = ''\n            else:\n                raise InvalidRequestError(message=\"messages: missing content\", param='messages')\n\n        # Validate multimodal content structure\n        if isinstance(content, list):\n            for item in content:\n                if not isinstance(item, dict) or 'type' not in item:\n                    raise InvalidRequestError(message=\"messages: invalid content item format\", param='messages')\n                if item['type'] not in ['text', 'image_url']:\n                    raise InvalidRequestError(message=\"messages: unsupported content type\", param='messages')\n                if item['type'] == 'text' and 'text' not in item:","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/oobabooga/textgen/blob/ed888c71f221df552750e1834b3654abab8ae345/modules/api/completions.py#L482-L518","documentation":"Messages with role 'function' (the legacy OpenAI mechanism for returning tool results) are rejected. The backend implements the modern tools API where results come back as role 'tool' messages with a 'tool_call_id', so the legacy role is explicitly refused with InvalidRequestError (400).","triggerScenarios":"POST /v1/chat/completions with a message {\"role\": \"function\", \"name\": \"get_weather\", \"content\": \"...\"}. Typically sent by old OpenAI SDK versions after executing a function call, or by hand-rolled agent loops following 2023 tutorials.","commonSituations":"Old openai<1.0 SDK chat loops; agent frameworks not yet migrated to the tools API; mixing a legacy client with the modern backend after fixing only 'functions'/'function_call'.","solutions":["Convert function results to the modern form: {\"role\": \"tool\", \"tool_call_id\": <id from assistant tool_calls>, \"content\": <result>}.","Upgrade to the modern loop: assistant message carries 'tool_calls', the tool reply uses role 'tool'.","Upgrade the OpenAI SDK to >=1.0, which emits role 'tool' natively."],"exampleFix":"# before\nmessages.append({\"role\": \"function\", \"name\": \"get_weather\", \"content\": \"21C\"})\n\n# after\nmessages.append({\"role\": \"tool\", \"tool_call_id\": assistant_msg[\"tool_calls\"][0][\"id\"], \"content\": \"21C\"})","handlingStrategy":"validation","validationCode":"def migrate_function_roles(messages):\n    out = []\n    for m in messages:\n        if m.get('role') == 'function':\n            out.append({'role': 'tool', 'tool_call_id': m.get('tool_call_id', m.get('name', 'call_0')), 'content': m.get('content', '')})\n        else:\n            out.append(m)\n    return out","typeGuard":"def has_legacy_function_role(messages) -> bool:\n    return any(isinstance(m, dict) and m.get('role') == 'function' for m in messages)","tryCatchPattern":"try:\n    resp = client.chat.completions.create(model=m, messages=msgs)\nexcept openai.BadRequestError as e:\n    if 'role: function is not supported' in str(e):\n        msgs = migrate_function_roles(msgs)\n        resp = client.chat.completions.create(model=m, messages=msgs)\n    else:\n        raise","preventionTips":["Use the modern loop: assistant tool_calls -> role 'tool' replies with tool_call_id.","Upgrade to openai>=1.0 SDK.","Add a pre-send lint that rejects role 'function'."],"tags":["openai-api","chat-completions","function-calling","validation"],"backgroundTag":null,"analyzedSha":"ed888c71f221df552750e1834b3654abab8ae345","analyzedAt":"2026-08-15T05:24:21.000Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}