{"record":{"id":"4b76489d9d59641e","repo":"spring-projects/spring-ai","slug":"invalid-neo4j-node-label-labels-must-start-wi","errorCode":null,"errorMessage":"Invalid Neo4j node label: ''. Labels must start with a letter or underscore and contain only letters, digits, or underscores.","messagePattern":"Invalid Neo4j node label: ''\\. Labels must start with a letter or underscore and contain only letters, digits, or underscores\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"memory-repositories/spring-ai-model-chat-memory-repository-neo4j/src/main/java/org/springframework/ai/chat/memory/repository/neo4j/Neo4jChatMemoryRepositoryConfig.java","lineNumber":113,"sourceCode":"\n\tprivate Neo4jChatMemoryRepositoryConfig(Builder builder) {\n\t\tAssert.state(builder.driver != null, \"driver must not be null\");\n\t\tthis.driver = builder.driver;\n\t\tthis.sessionLabel = builder.sessionLabel;\n\t\tthis.mediaLabel = builder.mediaLabel;\n\t\tthis.messageLabel = builder.messageLabel;\n\t\tthis.toolCallLabel = builder.toolCallLabel;\n\t\tthis.metadataLabel = builder.metadataLabel;\n\t\tthis.toolResponseLabel = builder.toolResponseLabel;\n\t\tvalidateLabels();\n\t\tensureIndexes();\n\t}\n\n\tprivate void validateLabels() {\n\t\tfor (String label : new String[] { this.sessionLabel, this.messageLabel, this.metadataLabel, this.mediaLabel,\n\t\t\t\tthis.toolCallLabel, this.toolResponseLabel }) {\n\t\t\tif (!SAFE_LABEL.matcher(label).matches()) {\n\t\t\t\tthrow new IllegalArgumentException(\"Invalid Neo4j node label: '\" + label\n\t\t\t\t\t\t+ \"'. Labels must start with a letter or underscore and contain only letters, digits, or underscores.\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Ensures that indexes exist on conversationId for Session nodes and index for\n\t * Message nodes. This improves query performance for lookups and ordering.\n\t */\n\tprivate void ensureIndexes() {\n\t\ttry (var session = this.driver.session()) {\n\t\t\t// Index for conversationId on Session nodes\n\t\t\tString sessionIndexCypher = String.format(\n\t\t\t\t\t\"CREATE INDEX session_conversation_id_index IF NOT EXISTS FOR (n:%s) ON (n.conversationId)\",\n\t\t\t\t\tthis.sessionLabel);\n\t\t\t// Index for index on Message nodes\n\t\t\tString messageIndexCypher = String\n\t\t\t\t.format(\"CREATE INDEX message_index_index IF NOT EXISTS FOR (n:%s) ON (n.index)\", this.messageLabel);","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/memory-repositories/spring-ai-model-chat-memory-repository-neo4j/src/main/java/org/springframework/ai/chat/memory/repository/neo4j/Neo4jChatMemoryRepositoryConfig.java#L95-L131","documentation":"Neo4jChatMemoryRepositoryConfig validates all configured node labels (session, message, metadata, media, toolCall, toolResponse) against a SAFE_LABEL regex: labels must start with a letter or underscore and contain only letters, digits, or underscores. An invalid label (here an empty string) throws IllegalArgumentException at construction time to prevent broken/unsafe Cypher label interpolation.","triggerScenarios":"Building Neo4jChatMemoryRepositoryConfig with a label set to \"\" (e.g. .sessionLabel(\"\") or a blank property bound from application.yml/properties).","commonSituations":"Empty spring.ai.chat.memory.repository.neo4j.* label properties in application.yml; environment-variable-driven config where the variable is unset and defaults to empty string; labels with spaces or hyphens.","solutions":["Set every label to a valid identifier, e.g. sessionLabel(\"session\"), messageLabel(\"message\")","Check application.yml/properties for empty label keys and give them values or remove the overrides","If using env vars, provide a default: ${NEO4J_MESSAGE_LABEL:message}"],"exampleFix":"// before\nNeo4jChatMemoryRepositoryConfig.builder().messageLabel(\"\").build();\n// after\nNeo4jChatMemoryRepositoryConfig.builder().messageLabel(\"message\").build();","handlingStrategy":"validation","validationCode":"Pattern SAFE = Pattern.compile(\"^[A-Za-z_][A-Za-z0-9_]*$\");\nif (label == null || !SAFE.matcher(label).matches()) {\n    throw new IllegalArgumentException(\"Label must match [A-Za-z_][A-Za-z0-9_]*\");\n}","typeGuard":"boolean isValidLabel(String l) { return l != null && l.matches(\"[A-Za-z_][A-Za-z0-9_]*\"); }","tryCatchPattern":"try { config = Neo4jChatMemoryRepositoryConfig.builder().messageLabel(label).build(); }\ncatch (IllegalArgumentException e) { config = Neo4jChatMemoryRepositoryConfig.builder().build(); } // defaults","preventionTips":["Give explicit valid values for all label properties; never leave them blank","Use env var defaults like ${NEO4J_MESSAGE_LABEL:message}","Avoid spaces and hyphens in Neo4j labels"],"tags":["neo4j","config","label-validation","illegal-argument"],"backgroundTag":"invalid-config-value","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"}