{"record":{"id":"dc25f8d8d2a221f3","repo":"apache/shenyu","slug":"failed-to-parse-request-body-into-chatcompletionrequest","errorCode":null,"errorMessage":"Failed to parse request body into ChatCompletionRequest","messagePattern":"Failed to parse request body into ChatCompletionRequest","errorType":"exception","errorClass":"ShenyuException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/protocol/OpenAiProtocolAdapter.java","lineNumber":146,"sourceCode":"            if (Objects.nonNull(fallbackConfig.getModel()) && !fallbackConfig.getModel().isEmpty()) {\n                mutableRoot.put(FIELD_MODEL, fallbackConfig.getModel());\n            }\n            if (Objects.nonNull(fallbackConfig.getTemperature())) {\n                mutableRoot.put(FIELD_TEMPERATURE, fallbackConfig.getTemperature());\n            }\n            if (Objects.nonNull(fallbackConfig.getMaxTokens())) {\n                mutableRoot.put(FIELD_MAX_TOKENS, fallbackConfig.getMaxTokens());\n                mutableRoot.put(FIELD_MAX_COMPLETION_TOKENS, fallbackConfig.getMaxTokens());\n            }\n        }\n\n        mutableRoot.put(FIELD_STREAM, stream);\n\n        try {\n            return MAPPER.treeToValue(mutableRoot, ChatCompletionRequest.class);\n        } catch (Exception e) {\n            LOG.error(\"[AiProxy] Failed to parse request body into ChatCompletionRequest\", e);\n            throw new ShenyuException(\"Failed to parse request body into ChatCompletionRequest\", e);\n        }\n    }\n\n    private static JsonNode parseStrict(final String json) {\n        try {\n            return MAPPER.readTree(json);\n        } catch (JsonProcessingException e) {\n            return null;\n        }\n    }\n}\n","sourceCodeStart":128,"sourceCodeEnd":158,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/protocol/OpenAiProtocolAdapter.java#L128-L158","documentation":"OpenAiProtocolAdapter.toChatCompletionRequest mutates the client's raw JSON tree (injecting stream flag etc.) and then binds it to the ChatCompletionRequest DTO with Jackson. If the body cannot be converted — wrong types, unknown/invalid fields with strict parsing, or malformed structure — a ShenyuException wrapping the Jackson failure is thrown and the request is rejected before proxying to the AI upstream.","triggerScenarios":"Calling toChatCompletionRequest with a JSON body whose fields don't match ChatCompletionRequest (e.g. \"model\": 123, \"messages\" as a non-array, or a structure that fails Jackson treeToValue after MAPPER.treeToValue).","commonSituations":"Clients posting OpenAI-compatible requests with subtly wrong field types, extra nested fields that fail strict binding, non-JSON or partially valid payloads, or a gateway version whose ChatCompletionRequest DTO is narrower than the fields the client sends.","solutions":["Log/inspect the wrapped Jackson cause to find the exact offending field, then fix the client request body field types/structure.","Validate the JSON body against the ChatCompletionRequest schema before sending it through the AI proxy.","If a legitimate field is rejected, relax the adapter (configure ObjectMapper deserialization failure handlers) or extend the ChatCompletionRequest DTO.","Confirm the Content-Type is application/json and the body is complete (not truncated by an upstream proxy)."],"exampleFix":"// before\ncurl -X POST gateway/chat -d '{\"model\": 1, \"messages\":\"hi\"}'\n// after\ncurl -X POST gateway/chat -H 'Content-Type: application/json' \\\n  -d '{\"model\":\"gpt-4o\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}'","handlingStrategy":"validation","validationCode":"const errs = [];\nif (typeof body.model !== 'string') errs.push('model must be a string');\nif (!Array.isArray(body.messages)) errs.push('messages must be an array');\nif (errs.length) throw new Error('invalid ChatCompletionRequest: ' + errs.join('; '));","typeGuard":"function isChatCompletionRequest(b) {\n  return !!b && typeof b === 'object' && typeof b.model === 'string' && Array.isArray(b.messages);\n}","tryCatchPattern":"try {\n  adapter.toChatCompletionRequest(rawBody);\n} catch (ShenyuException e) {\n  log.error(\"bad AI request body: {}\", e.getCause(), e);\n  return ResponseEntity.badRequest().body(\"invalid request body\");\n}","preventionTips":["Validate request bodies against the OpenAI chat-completion schema at the edge.","Always send Content-Type: application/json with complete, non-truncated bodies.","Keep the gateway's ChatCompletionRequest DTO in sync with the fields your clients actually send."],"tags":["json","ai-proxy","request-validation"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}