apache/shenyu · error · ShenyuException
Failed to parse request body into ChatCompletionRequest
Error message
Failed to parse request body into ChatCompletionRequest
What it means
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.
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).
Example fix
// before
curl -X POST gateway/chat -d '{"model": 1, "messages":"hi"}'
// after
curl -X POST gateway/chat -H 'Content-Type: application/json' \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}' Defensive patterns
Strategy: validation
Validate before calling
const errs = [];
if (typeof body.model !== 'string') errs.push('model must be a string');
if (!Array.isArray(body.messages)) errs.push('messages must be an array');
if (errs.length) throw new Error('invalid ChatCompletionRequest: ' + errs.join('; ')); Type guard
function isChatCompletionRequest(b) {
return !!b && typeof b === 'object' && typeof b.model === 'string' && Array.isArray(b.messages);
} Try / catch
try {
adapter.toChatCompletionRequest(rawBody);
} catch (ShenyuException e) {
log.error("bad AI request body: {}", e.getCause(), e);
return ResponseEntity.badRequest().body("invalid request body");
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid Swagger JSON format:
- The date should be a string value
- Invalid request body: expected a JSON object
- baseUrl must not be empty
- apiKey must not be empty
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/dc25f8d8d2a221f3.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/protocol/OpenAiProtocolAdapter.java:146
if (Objects.nonNull(fallbackConfig.getModel()) && !fallbackConfig.getModel().isEmpty()) {
mutableRoot.put(FIELD_MODEL, fallbackConfig.getModel());
}
if (Objects.nonNull(fallbackConfig.getTemperature())) {
mutableRoot.put(FIELD_TEMPERATURE, fallbackConfig.getTemperature());
}
if (Objects.nonNull(fallbackConfig.getMaxTokens())) {
mutableRoot.put(FIELD_MAX_TOKENS, fallbackConfig.getMaxTokens());
mutableRoot.put(FIELD_MAX_COMPLETION_TOKENS, fallbackConfig.getMaxTokens());
}
}
mutableRoot.put(FIELD_STREAM, stream);
try {
return MAPPER.treeToValue(mutableRoot, ChatCompletionRequest.class);
} catch (Exception e) {
LOG.error("[AiProxy] Failed to parse request body into ChatCompletionRequest", e);
throw new ShenyuException("Failed to parse request body into ChatCompletionRequest", e);
}
}
private static JsonNode parseStrict(final String json) {
try {
return MAPPER.readTree(json);
} catch (JsonProcessingException e) {
return null;
}
}
}
View on GitHub (pinned to 567142e072)