{"record":{"id":"f3268ba3f3340de3","repo":"JuliusBrussee/caveman","slug":"expected-openai-chat-or-openai-responses-protocol","errorCode":null,"errorMessage":"Expected openai-chat or openai-responses protocol","messagePattern":"Expected openai-chat or openai-responses protocol","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/openai.py","lineNumber":64,"sourceCode":"    \"\"\"\n    client: object\n    functions: object\n    _definitions: str\n\n    @property\n    def tools(self):\n        return json.loads(self._definitions)\n\n\ndef with_caveman_openai_tools(client, *, runtime, scope, protocol, tools, functions, transport=None):\n    \"\"\"Bind real application dispatch without introducing a second scheduler.\n\n    ``protocol`` is ``openai-chat`` or ``openai-responses``. ``tools`` contains\n    the corresponding native definitions; ``functions`` maps each native tool\n    name to a callable accepting the decoded arguments dictionary.\n    \"\"\"\n    if protocol not in (\"openai-chat\", \"openai-responses\"):\n        raise ValueError(\"Expected openai-chat or openai-responses protocol\")\n    definitions = copy.deepcopy(list(tools))\n    if not plain(functions) or any(type(name) is not str or not callable(fn) for name, fn in functions.items()):\n        raise TypeError(\"functions must map native tool names to callables\")\n    names = []\n    for definition in definitions:\n        tool = definition.get(\"function\") if plain(definition) and protocol == \"openai-chat\" else definition\n        if not plain(definition) or definition.get(\"type\") != \"function\" or not plain(tool) or type(tool.get(\"name\")) is not str:\n            raise TypeError(\"Expected native client function tool definitions\")\n        names.append(tool[\"name\"])\n    if len(set(names)) != len(names) or \"caveman_retrieve\" in names or \"caveman_retrieve\" in functions:\n        raise ValueError(\"Duplicate or reserved caveman_retrieve tool name\")\n    if set(names) != set(functions):\n        raise ValueError(\"Every native function definition needs exactly one executor\")\n    if runtime.mode != \"compress\" or not in_range(__version__, \"3.10\", \"4\"):\n        return CavemanOpenAIToolLoop(with_caveman_openai(client, runtime=runtime, scope=scope, transport=transport), MappingProxyType(dict(functions)), json.dumps(definitions))\n    binding = runtime.recovery(scope)\n    tool = {\"name\": binding.name, \"description\": binding.description, \"parameters\": copy.deepcopy(binding.input_schema)}\n    definition = {\"type\": \"function\", \"function\": tool} if protocol == \"openai-chat\" else {\"type\": \"function\", **tool}","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/openai.py#L46-L82","documentation":"with_caveman_openai_tools accepts only the two supported wire protocols: `openai-chat` (tools nested under a `function` key) and `openai-responses` (flat tool objects). Any other protocol string raises ValueError before any tools are processed.","triggerScenarios":"Calling with_caveman_openai_tools(protocol=\"chat\"), protocol=\"openai\", protocol=\"completions\", or a misspelled/None protocol value.","commonSituations":"Copy-pasting adapter setup from Anthropic or generic docs; a config value for the API surface was never normalized; mixing up client SDKs (responses vs chat completions).","solutions":["Pass protocol=\"openai-chat\" when using client.chat.completions-style tool definitions","Pass protocol=\"openai-responses\" when using the Responses API tool shape","Fix typos and trim/normalize the protocol string before the call"],"exampleFix":"// before\nloop = with_caveman_openai_tools(client, runtime, scope, protocol=\"openai\", tools=tools, functions=functions)\n// after\nloop = with_caveman_openai_tools(client, runtime, scope, protocol=\"openai-chat\", tools=tools, functions=functions)","handlingStrategy":"validation","validationCode":"PROTOCOL = \"openai-chat\" if use_chat_completions else \"openai-responses\"\nassert PROTOCOL in (\"openai-chat\", \"openai-responses\")","typeGuard":"def is_openai_protocol(p): return p in (\"openai-chat\", \"openai-responses\")","tryCatchPattern":"try:\n    loop = with_caveman_openai_tools(client, runtime, scope, protocol=protocol, tools=tools, functions=functions)\nexcept ValueError as e:\n    if \"protocol\" in str(e):\n        protocol = \"openai-chat\"\n        loop = with_caveman_openai_tools(client, runtime, scope, protocol=protocol, tools=tools, functions=functions)\n    else:\n        raise","preventionTips":["Define the protocol string once as a module constant","Pick protocol based on which OpenAI API surface your tools were shaped for","Never pass user/config input unvalidated as protocol"],"tags":["python","validation","openai"],"backgroundTag":"invalid-enum-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}