{"record":{"id":"be88c4ee7e7591f6","repo":"spring-projects/spring-ai","slug":"textcontent-and-resource-cannot-be-set-at-the-same","errorCode":null,"errorMessage":"textContent and resource cannot be set at the same time","messagePattern":"textContent and resource cannot be set at the same time","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-model/src/main/java/org/springframework/ai/chat/messages/SystemMessage.java","lineNumber":116,"sourceCode":"\n\t\tpublic Builder text(String textContent) {\n\t\t\tthis.textContent = textContent;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder text(Resource resource) {\n\t\t\tthis.resource = resource;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder metadata(Map<String, Object> metadata) {\n\t\t\tthis.metadata = metadata;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic SystemMessage build() {\n\t\t\tif (StringUtils.hasText(this.textContent) && this.resource != null) {\n\t\t\t\tthrow new IllegalArgumentException(\"textContent and resource cannot be set at the same time\");\n\t\t\t}\n\t\t\telse if (this.resource != null) {\n\t\t\t\tthis.textContent = MessageUtils.readResource(this.resource);\n\t\t\t}\n\t\t\treturn new SystemMessage(this.textContent, this.metadata);\n\t\t}\n\n\t}\n\n}\n","sourceCodeStart":98,"sourceCodeEnd":127,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-model/src/main/java/org/springframework/ai/chat/messages/SystemMessage.java#L98-L127","documentation":"SystemMessage.Builder.build() enforces mutual exclusivity between textContent and resource: you may supply text directly or load it from a resource, never both. Supplying both throws IllegalArgumentException before any message is created.","triggerScenarios":"Calling SystemMessage.builder().text(\"...\").resource(myResource).build(); i.e. chaining both the text and resource setters on the same builder.","commonSituations":"Copy-pasting builder code that already sets text then adding a resource-based prompt loader; conditional code paths where both branches accidentally execute.","solutions":["Remove either the text(...) or the resource(...) call — keep exactly one","If the resource should win, skip setting text; if the text should win, drop the resource call","Load the resource yourself with MessageUtils.readResource and pass it via text(...) if you need pre-processing"],"exampleFix":"// before\nSystemMessage msg = SystemMessage.builder()\n    .text(\"You are helpful\").resource(new ClassPathResource(\"sys.txt\")).build();\n// after\nSystemMessage msg = SystemMessage.builder()\n    .resource(new ClassPathResource(\"sys.txt\")).build();","handlingStrategy":"validation","validationCode":"if (StringUtils.hasText(text) && resource != null) {\n    throw new IllegalArgumentException(\"Set either text or resource, not both\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    SystemMessage msg = builder.build();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"textContent and resource\")) { /* rebuild with a single source */ }\n    throw e;\n}","preventionTips":["Audit builder chains for both text(...) and resource(...) calls","Centralize prompt construction in one helper that picks a single source","Add unit tests asserting the mutual-exclusion rule"],"tags":["builder","validation","system-message","spring-ai"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}