{"record":{"id":"d9cbae7f007031fd","repo":"microsoft/autogen","slug":"json-output-must-be-a-boolean-a-basemodel-subclas","errorCode":null,"errorMessage":"json_output must be a boolean, a BaseModel subclass or None.","messagePattern":"json_output must be a boolean, a BaseModel subclass or None\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py","lineNumber":305,"sourceCode":"            if isinstance(msg, SystemMessage):\n                converted_messages.append({\"role\": \"system\", \"content\": msg.content})\n            elif isinstance(msg, UserMessage) and isinstance(msg.content, str):\n                converted_messages.append({\"role\": \"user\", \"content\": msg.content})\n            elif isinstance(msg, AssistantMessage) and isinstance(msg.content, str):\n                converted_messages.append({\"role\": \"assistant\", \"content\": msg.content})\n            elif (\n                isinstance(msg, SystemMessage) or isinstance(msg, UserMessage) or isinstance(msg, AssistantMessage)\n            ) and isinstance(msg.content, list):\n                raise ValueError(\"Multi-part messages such as those containing images are currently not supported.\")\n            else:\n                raise ValueError(f\"Unsupported message type: {type(msg)}\")\n\n        if isinstance(json_output, type) and issubclass(json_output, BaseModel):\n            create_args[\"response_format\"] = {\"type\": \"json_object\", \"schema\": json_output.model_json_schema()}\n        elif json_output is True:\n            create_args[\"response_format\"] = {\"type\": \"json_object\"}\n        elif json_output is not False and json_output is not None:\n            raise ValueError(\"json_output must be a boolean, a BaseModel subclass or None.\")\n\n        # Handle tool_choice parameter\n        if tool_choice != \"auto\":\n            warnings.warn(\n                \"tool_choice parameter is specified but LlamaCppChatCompletionClient does not support it. \"\n                \"This parameter will be ignored.\",\n                UserWarning,\n                stacklevel=2,\n            )\n\n        if self.model_info[\"function_calling\"]:\n            # Run this in on the event loop to avoid blocking.\n            response_future = asyncio.get_event_loop().run_in_executor(\n                None,\n                lambda: self.llm.create_chat_completion(\n                    messages=converted_messages, tools=convert_tools(tools), stream=False, **create_args\n                ),\n            )","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py#L287-L323","documentation":"In create(), json_output is validated to be exactly True/False, None, or a BaseModel subclass. Anything else (an int, a string, a BaseModel *instance* instead of the class, a dataclass) hits the elif chain's final raise. Note the message text omits the instance case, but passing an instance is the most common way to land here because isinstance(json_output, type) is False for instances.","triggerScenarios":"create(messages, json_output=MyModel()) — instance instead of class; json_output=1 or json_output='json'; passing a pydantic dataclass or TypedDict class; forwarding an unvalidated config value into json_output.","commonSituations":"Dynamically deserializing config (YAML/JSON) where json_output arrives as the string 'true'; passing a schema instance built elsewhere; copy-paste from code that used a different client whose API takes an instance.","solutions":["Pass the class, not an instance: json_output=MyModel (no parentheses)","Pass json_output=True for plain JSON mode, False or None to disable","If json_output comes from config, coerce it first: json_output = {'true': True, 'false': False}.get(str(cfg).lower(), cfg)"],"exampleFix":"# before\nresult = await client.create(messages, json_output=StepPlan())  # instance -> ValueError\n\n# after\nresult = await client.create(messages, json_output=StepPlan)  # the class itself","handlingStrategy":"type-guard","validationCode":"from pydantic import BaseModel\n\ndef coerce_json_output(v: object) -> bool | type[BaseModel] | None:\n    if v is None or isinstance(v, bool):\n        return v\n    if isinstance(v, type) and issubclass(v, BaseModel):\n        return v\n    if isinstance(v, str) and v.lower() in (\"true\", \"false\"):\n        return v.lower() == \"true\"\n    raise TypeError(f\"json_output cannot be {type(v)!r}\")\n\nresult = await client.create(messages, json_output=coerce_json_output(cfg[\"json_output\"]))","typeGuard":"def is_valid_json_output(v: object) -> TypeGuard[bool | type[BaseModel] | None]:\n    return v is None or isinstance(v, bool) or (isinstance(v, type) and issubclass(v, BaseModel))","tryCatchPattern":"try:\n    result = await client.create(messages, json_output=jo)\nexcept ValueError as e:\n    if \"json_output must be\" in str(e):\n        result = await client.create(messages, json_output=bool(jo))\n    else:\n        raise","preventionTips":["Always pass the BaseModel class, never an instance","Type your wrappers' json_output as Optional[bool | type[BaseModel]] so mypy/pyright catch misuse","Parse config booleans into real bools before forwarding"],"tags":["llama-cpp","json-output","validation","api-misuse"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}