{"record":{"id":"5317dc9da4c32c7f","repo":"spring-projects/spring-ai","slug":"failed-to-parse-json-schema","errorCode":null,"errorMessage":"Failed to parse JSON schema: ","messagePattern":"Failed to parse JSON schema: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java","lineNumber":791,"sourceCode":"\t\t\t\tString jsonSchemaString = responseFormat.getJsonSchema() != null ? responseFormat.getJsonSchema() : \"\";\n\t\t\t\ttry {\n\n\t\t\t\t\tResponseFormatJsonSchema.JsonSchema.Builder jsonSchemaBuilder = ResponseFormatJsonSchema.JsonSchema\n\t\t\t\t\t\t.builder();\n\t\t\t\t\tjsonSchemaBuilder.name(\"json_schema\");\n\t\t\t\t\tBoolean strict = responseFormat.getStrict();\n\t\t\t\t\tjsonSchemaBuilder.strict(strict != null ? strict : true);\n\n\t\t\t\t\tResponseFormatJsonSchema.JsonSchema.Schema schema = objectMapper.readValue(jsonSchemaString,\n\t\t\t\t\t\t\tResponseFormatJsonSchema.JsonSchema.Schema.class);\n\n\t\t\t\t\tjsonSchemaBuilder.schema(schema);\n\n\t\t\t\t\tbuilder.responseFormat(\n\t\t\t\t\t\t\tResponseFormatJsonSchema.builder().jsonSchema(jsonSchemaBuilder.build()).build());\n\t\t\t\t}\n\t\t\t\tcatch (Exception e) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to parse JSON schema: \" + jsonSchemaString, e);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new IllegalArgumentException(\"Unsupported response format type: \" + responseFormat.getType());\n\t\t\t}\n\t\t}\n\t\tif (requestOptions.getSeed() != null) {\n\t\t\tbuilder.seed(requestOptions.getSeed());\n\t\t}\n\t\tif (requestOptions.getStop() != null && !requestOptions.getStop().isEmpty()) {\n\t\t\tif (requestOptions.getStop().size() == 1) {\n\t\t\t\tbuilder.stop(ChatCompletionCreateParams.Stop.ofString(requestOptions.getStop().get(0)));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbuilder.stop(ChatCompletionCreateParams.Stop.ofStrings(requestOptions.getStop()));\n\t\t\t}\n\t\t}\n\t\tif (requestOptions.getTemperature() != null) {","sourceCodeStart":773,"sourceCodeEnd":809,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java#L773-L809","documentation":"When OpenAiChatOptions.responseFormat has type JSON_SCHEMA, createRequest parses responseFormat.getJsonSchema() (a JSON string) into the SDK's JsonSchema object via Jackson. Any exception during parsing — malformed JSON, a structure that doesn't deserialize into ResponseFormatJsonSchema.JsonSchema.Schema, or a null/empty string coerced to \"\" — is wrapped and rethrown as this IllegalArgumentException with the offending schema string as the message and the original cause attached.","triggerScenarios":"Setting OpenAiChatOptions.builder().responseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, schemaJson)) where schemaJson is not valid JSON, is an empty/null string (defaults to \"\"), uses JSON constructs Jackson cannot map to the Schema type (e.g. top-level non-object), or contains invalid JSON Schema keywords that break deserialization.","commonSituations":"Hand-editing a schema and leaving a trailing comma; building the schema string with string concatenation producing invalid JSON; passing a Java object's toString() instead of JSON; forgetting to set getJsonSchema() so it parses \"\"; loading the schema from a file/resource that failed to load and returned null.","solutions":["Validate that the jsonSchema string is well-formed JSON before setting it: parse it with ObjectMapper.readTree() in a pre-check.","Ensure the string is a JSON Schema object starting with '{' (e.g. {\"type\":\"object\",\"properties\":{...}}), not null, empty, or a non-object JSON value.","Generate the schema string with a serializer (new ObjectMapper().writeValueAsString(schemaMap)) instead of hand-writing or concatenating it.","Read the wrapped cause exception (e.getCause()) — it pinpoints the exact Jackson deserialization problem, e.g. unexpected token or unmapped field."],"exampleFix":"// before\noptions.setResponseFormat(new ResponseFormat(JSON_SCHEMA, \"{type: 'object'}\")); // invalid JSON (single quotes)\n\n// after\nString schema = \"{\\\"type\\\":\\\"object\\\",\\\"properties\\\":{\\\"city\\\":{\\\"type\\\":\\\"string\\\"}},\\\"required\\\":[\\\"city\\\"]}\";\nnew ObjectMapper().readTree(schema); // validate first\noptions.setResponseFormat(new ResponseFormat(JSON_SCHEMA, schema));","handlingStrategy":"validation","validationCode":"public static String validateJsonSchema(String schema) {\n    if (schema == null || schema.isBlank()) throw new IllegalArgumentException(\"jsonSchema must be a non-empty JSON object\");\n    try {\n        com.fasterxml.jackson.databind.JsonNode n = new com.fasterxml.jackson.databind.ObjectMapper().readTree(schema);\n        if (!n.isObject()) throw new IllegalArgumentException(\"jsonSchema must be a JSON object\");\n    } catch (com.fasterxml.jackson.core.JsonProcessingException e) {\n        throw new IllegalArgumentException(\"jsonSchema is not valid JSON\", e);\n    }\n    return schema;\n}","typeGuard":null,"tryCatchPattern":"try {\n    return chatModel.call(prompt);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Failed to parse JSON schema:\")) {\n        logger.error(\"Check the schema string and cause\", e.getCause());\n        prompt.getOptions().setResponseFormat(new ResponseFormat(ResponseFormat.Type.JSON_OBJECT, null));\n        return chatModel.call(prompt);\n    }\n    throw e;\n}","preventionTips":["Serialize schemas from typed objects/maps with Jackson instead of hand-writing JSON strings.","Validate the schema string with readTree() before assigning it to ResponseFormat.","Never pass null or empty jsonSchema when type is JSON_SCHEMA (it defaults to \"\" and fails).","Log e.getCause() when this error fires — the Jackson message pinpoints the offending token."],"tags":["openai","spring-ai","json-schema","json-parse","structured-output"],"backgroundTag":"json-parse-error","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"}