{"record":{"id":"8e7f0a8f04e70187","repo":"openai/openai-python","slug":"unexpected-json-data-type-type-json-data-cann","errorCode":null,"errorMessage":"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`","messagePattern":"Unexpected JSON data type, (.+?), cannot merge with `extra_body`","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/openai/_base_client.py","lineNumber":538,"sourceCode":"        *,\n        retries_taken: int = 0,\n    ) -> httpx2.Request:\n        # Request bodies, files, URLs, and custom options can contain private data.\n        log.debug(\n            \"Building HTTP request: method=%s retries_taken=%i\",\n            get_http_method_for_logging(options.method),\n            retries_taken,\n        )\n        kwargs: dict[str, Any] = {}\n\n        json_data = options.json_data\n        if options.extra_json is not None:\n            if json_data is None:\n                json_data = cast(Body, options.extra_json)\n            elif is_mapping(json_data):\n                json_data = _merge_mappings(json_data, options.extra_json)\n            else:\n                raise RuntimeError(f\"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`\")\n\n        headers = self._build_headers(options, retries_taken=retries_taken)\n        params = _merge_mappings({**self._auth_query(options.security), **self.default_query}, options.params)\n        content_type = headers.get(\"Content-Type\")\n        files = options.files\n\n        # If the given Content-Type header is multipart/form-data then it\n        # has to be removed so that httpx can generate the header with\n        # additional information for us as it has to be in this form\n        # for the server to be able to correctly parse the request:\n        # multipart/form-data; boundary=---abc--\n        if content_type is not None and content_type.startswith(\"multipart/form-data\"):\n            if \"boundary\" not in content_type:\n                # only remove the header if the boundary hasn't been explicitly set\n                # as the caller doesn't want httpx to come up with their own boundary\n                headers.pop(\"Content-Type\")\n\n            # As we are now sending multipart/form-data instead of application/json","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/openai/openai-python/blob/9917c6e28e66e90e1227b3d223c06a8c5441515a/src/openai/_base_client.py#L520-L556","documentation":"The OpenAI Python SDK raises this ValueError when await client.vector_stores.file_batches.retrieve() (async) is called with a falsy vector_store_id. It is a client-side pre-flight check so the GET /vector_stores/{vector_store_id}/file_batches/{batch_id} request is never sent with an invalid path segment.","triggerScenarios":"Awaiting retrieve(vector_store_id='', batch_id='vsfb_...') or vector_store_id=None on the async resource, e.g. when polling batch status with a store ID that came back empty.","commonSituations":"Polling loops that read IDs from dictionaries with missing keys, deserialized job records with absent store fields, or tests using placeholder empty strings.","solutions":["Fetch and pass the actual 'vs_...' store identifier; verify the variable is populated before the retrieve call.","Use dict.get() carefully: distinguish a missing key from an empty value and re-fetch the store ID if absent.","In polling loops, validate both IDs once before entering the loop."],"exampleFix":"# before\nbatch = await client.vector_stores.file_batches.retrieve(vector_store_id=job[\"store\"], batch_id=job[\"batch\"])  # job[\"store\"] == \"\"\n\n# after\nvs = job.get(\"store\") or (await client.vector_stores.create(name=\"docs\")).id\nbatch = await client.vector_stores.file_batches.retrieve(vector_store_id=vs, batch_id=job[\"batch\"])","handlingStrategy":"validation","validationCode":"vs = job.get(\"vector_store_id\")\nif not vs:\n    raise ValueError(\"job record is missing vector_store_id\")\nbatch = await client.vector_stores.file_batches.retrieve(vector_store_id=vs, batch_id=job[\"batch_id\"])","typeGuard":"def is_valid_vector_store_id(v: object) -> bool:\n    return isinstance(v, str) and v.startswith(\"vs_\") and len(v) > 3","tryCatchPattern":null,"preventionTips":["Validate both IDs once before entering a polling loop.","Treat missing dict keys as errors, not empty defaults."],"tags":["openai","vector-stores","file-batches","async","validation","python"],"backgroundTag":"empty-required-parameter","analyzedSha":"9917c6e28e66e90e1227b3d223c06a8c5441515a","analyzedAt":"2026-08-28T11:46:34.183Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}