{"record":{"id":"890e1ef13c942261","repo":"sgl-project/sglang","slug":"tool-choice-required-or-a-named-tool-cannot-be-c","errorCode":null,"errorMessage":"tool_choice 'required' or a named tool cannot be combined with response_format, regex, or ebnf: the tool-call constraint and the output constraint cannot both be honored.","messagePattern":"tool_choice 'required' or a named tool cannot be combined with response_format, regex, or ebnf: the tool-call constraint and the output constraint cannot both be honored\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/entrypoints/openai/protocol.py","lineNumber":1154,"sourceCode":"            sampling_params[\"json_schema\"] = '{\"type\": \"object\"}'\n        elif self.response_format and self.response_format.type == \"structural_tag\":\n            sampling_params[\"structural_tag\"] = convert_json_schema_to_str(\n                self.response_format.model_dump(by_alias=True)\n            )\n\n        # Check if there are already existing output constraints\n        has_existing_constraints = (\n            sampling_params.get(\"regex\")\n            or sampling_params.get(\"ebnf\")\n            or sampling_params.get(\"structural_tag\")\n            or sampling_params.get(\"json_schema\")\n        )\n\n        if tool_call_constraint and has_existing_constraints:\n            if self.tool_choice == \"required\" or isinstance(\n                self.tool_choice, ToolChoice\n            ):\n                raise ValueError(\n                    \"tool_choice 'required' or a named tool cannot be combined with \"\n                    \"response_format, regex, or ebnf: the tool-call constraint and the \"\n                    \"output constraint cannot both be honored.\"\n                )\n            logger.warning(\"Constrained decoding is not compatible with tool calls.\")\n        elif tool_call_constraint:\n            constraint_type, constraint_value = tool_call_constraint\n            if constraint_type == \"structural_tag\":\n                sampling_params[constraint_type] = convert_json_schema_to_str(\n                    constraint_value.model_dump(by_alias=True)\n                )\n            elif constraint_type == \"json_schema\":\n                sampling_params[constraint_type] = convert_json_schema_to_str(\n                    constraint_value  # type: ignore\n                )\n            else:\n                sampling_params[constraint_type] = constraint_value\n","sourceCodeStart":1136,"sourceCodeEnd":1172,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/openai/protocol.py#L1136-L1172","documentation":"to_sampling_params refuses requests that simultaneously force tool calls (tool_choice='required' or a named ToolChoice object) and impose an output constraint (response_format, regex, or ebnf). The grammar for tool-call structure and the user grammar cannot both be satisfied, so the request is rejected rather than silently misbehaving. With tool_choice='auto' it only logs a warning and drops the constraint.","triggerScenarios":"POST /v1/chat/completions with tools=[...] plus tool_choice=\"required\" (or {\"type\":\"function\",\"function\":{\"name\":\"f\"}}) and also response_format={\"type\":\"json_schema\",...} or regex/ebnf fields.","commonSituations":"Developers wanting schema-guaranteed tool arguments trying to also pass a json_schema response_format; migrating from clients that set response_format globally on all requests; combining structured output examples with function calling examples.","solutions":["Remove response_format/regex/ebnf and rely on the tool's parameters JSON schema to constrain arguments","Or switch tool_choice to \"auto\"/\"none\" if structured output, not tool calling, is the real goal","Express argument constraints inside the tool definition's JSON schema instead"],"exampleFix":"// before\n{\"tools\": [...], \"tool_choice\": \"required\", \"response_format\": {\"type\": \"json_object\"}}\n// after\n{\"tools\": [...], \"tool_choice\": \"required\"}","handlingStrategy":"validation","validationCode":"forced = body.get(\"tool_choice\") in (\"required\",) or isinstance(body.get(\"tool_choice\"), dict)\nconstrained = any(body.get(k) for k in (\"response_format\", \"regex\", \"ebnf\"))\nif forced and constrained:\n    del body[\"response_format\"]; body.pop(\"regex\", None); body.pop(\"ebnf\", None)","typeGuard":"def has_forced_tool_choice(tc): return tc == \"required\" or (isinstance(tc, dict) and tc.get(\"type\") == \"tool\")","tryCatchPattern":"try: create(...)\nexcept ValueError as e: if 'cannot be combined' in str(e): retry without response_format","preventionTips":["Keep structured-output and forced-tool-call code paths separate","Constrain arguments via tool parameter schemas instead of response_format"],"tags":["openai-api","tool-calling","structured-output","constrained-decoding","sglang"],"backgroundTag":"conflicting-request-options","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}