{"record":{"id":"d10e8318911c8368","repo":"spring-projects/spring-ai","slug":"json-validation-failed-validationresponse","errorCode":null,"errorMessage":"JSON validation failed: ${validationResponse}","messagePattern":"JSON validation failed: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/StructuredOutputValidationAdvisor.java","lineNumber":157,"sourceCode":"\n\t\t\t// We should not validate tool call requests, only the content of the final\n\t\t\t// response.\n\t\t\tif (chatResponse == null || !chatResponse.hasToolCalls()) {\n\t\t\t\tSchemaValidation validationResponse = validateOutputSchema(chatClientResponse,\n\t\t\t\t\t\tcurrentAttemptNumber - 1);\n\n\t\t\t\tisValidationSuccess = validationResponse.success();\n\n\t\t\t\tif (!isValidationSuccess) {\n\n\t\t\t\t\t// Add the validation error message to the next user message\n\t\t\t\t\t// to let the LLM fix its output.\n\t\t\t\t\t// Note: We could also consider adding the previous invalid output.\n\t\t\t\t\t// However, this might lead to confusion and more complex prompts.\n\t\t\t\t\t// Instead, we rely on the LLM to generate a new output based on the\n\t\t\t\t\t// validation error.\n\t\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\t\tlogger.warn(\"JSON validation failed: \" + validationResponse);\n\t\t\t\t\t}\n\n\t\t\t\t\tString validationErrorMessage = \"Output JSON validation failed because of: \"\n\t\t\t\t\t\t\t+ validationResponse.errorMessage();\n\n\t\t\t\t\tPrompt augmentedPrompt = chatClientRequest.prompt()\n\t\t\t\t\t\t.augmentUserMessage(userMessage -> userMessage.mutate()\n\t\t\t\t\t\t\t.text(userMessage.getText() + System.lineSeparator() + validationErrorMessage)\n\t\t\t\t\t\t\t.build());\n\n\t\t\t\t\tprocessedChatClientRequest = chatClientRequest.mutate().prompt(augmentedPrompt).build();\n\t\t\t\t}\n\t\t\t\telse if (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"JSON validation succeeded\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/advisor/StructuredOutputValidationAdvisor.java#L139-L175","documentation":"StructuredOutputValidationAdvisor validates LLM JSON output against the requested schema after the call. When validation fails on an attempt (and attempts remain), it logs a warning with the SchemaValidation result and augments the prompt with the validation error so the LLM can retry and produce corrected JSON. This is a mid-flight retry signal, not a terminal failure, unless attempts run out.","triggerScenarios":"Using StructuredOutputValidationAdvisor (via ChatClient .entity(...) with a JSON schema) when the model's textual output fails schema/JSON validation — e.g. missing required fields, wrong types, or invalid JSON, often because the model ignored the format instructions.","commonSituations":"Small models producing non-conforming JSON; prompts that encourage prose around JSON; complex nested schemas the model struggles to satisfy on the first attempt.","solutions":["Improve the format instructions / schema description in the prompt so the model produces conforming JSON","Use a model better at structured output or a provider's native JSON/structured-output mode","Increase the advisor's max attempts so the retry loop can converge","Parse the final validation error and, if it still fails, fall back to manual parsing/repair of the output"],"exampleFix":"// before\nChatClient.create(chatModel).prompt().user(question)\n    .advisors(new StructuredOutputValidationAdvisor(1)) // no retries\n    .call().entity(MyRecord.class);\n// after\n.advisors(new StructuredOutputValidationAdvisor(3)) // allow fix-up retries","handlingStrategy":"try-catch","validationCode":"// client-side schema check on model output before trusting it\nObjectMapper om = new ObjectMapper();\nJsonNode node = om.readTree(outputText);\nSet<ValidationMessage> errs = schema.validate(node);\nif (!errs.isEmpty()) { /* augment prompt and retry */ }","typeGuard":null,"tryCatchPattern":"try {\n    MyRecord r = client.prompt().user(q)\n        .advisors(new StructuredOutputValidationAdvisor(3))\n        .call().entity(MyRecord.class);\n} catch (IllegalStateException e) {\n    // attempts exhausted; log e.getMessage() with validation detail\n}","preventionTips":["Give the model explicit format instructions and a simple schema","Increase advisor maxAttempts","Use providers with native structured output","Keep schemas small and flat where possible"],"tags":["json","schema","advisor","structured-output"],"backgroundTag":"schema-validation-failed","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}