cocoindex-io/cocoindex · error · ValueError
Invalid Neo4j {kind}: {name!r}. Must match [a-zA-Z_][a-zA-Z0
Error message
Invalid Neo4j {kind}: {name!r}. Must match [a-zA-Z_][a-zA-Z0-9_]*. What it means
Cypher cannot parameter-bind labels, relationship types, property names, or index names — they must be interpolated into the query string. validate_identifier rejects any name not matching ^[a-zA-Z_][a-zA-Z0-9_]*$ at API entry, preventing both malformed Cypher and Cypher injection from untrusted names.
Source
Thrown at python/cocoindex/connectors/neo4j/_cypher.py:52
"constraint_name",
"index_name",
"validate_identifier",
"vector_index_name",
]
IDENTIFIER_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
def validate_identifier(name: str, kind: str) -> None:
"""Reject anything that isn't ``[a-zA-Z_][a-zA-Z0-9_]*``.
Cypher labels, property names, and index names cannot be parameter-bound,
so untrusted names must be validated at API entry — never escaped at query
construction time.
"""
if not IDENTIFIER_RE.match(name):
raise ValueError(
f"Invalid Neo4j {kind}: {name!r}. Must match [a-zA-Z_][a-zA-Z0-9_]*."
)
def _quote(name: str) -> str:
"""Backtick-wrap an already-validated identifier for inline use in Cypher."""
return f"`{name}`"
def _key_clause(prefix: str, fields: Sequence[str]) -> str:
"""Build ``{<f1>: $<prefix>_0, <f2>: $<prefix>_1, ...}`` for a MATCH/MERGE pattern."""
parts = [f"{_quote(f)}: ${prefix}_{i}" for i, f in enumerate(fields)]
return "{" + ", ".join(parts) + "}"
def index_name(kind: str, label: str, fields: Sequence[str]) -> str:
"""Deterministic index name for a (kind, label, fields) triple.
View on GitHub (pinned to e84aa99b32)
Solutions
- Rename the label/type/property to match [a-zA-Z_][a-zA-Z0-9_]* (replace '-' and '.' with '_')
- Sanitize/normalize dynamic names (e.g. re.sub(r'[^A-Za-z0-9_]', '_', name)) and reject empty results before calling the connector
- Use a fixed literal label in code instead of deriving it from untrusted input
- Validate early with the same regex (IDENTIFIER_RE) at config-load time
Example fix
// before
label = f"doc-{file_ext}" # 'doc-pdf' fails validation
// after
import re
label = "doc_" + re.sub(r"[^A-Za-z0-9_]", "_", file_ext)
from cocoindex.connectors.neo4j._cypher import validate_identifier
validate_identifier(label, "label") # passes Defensive patterns
Strategy: validation
Validate before calling
import re
IDENTIFIER_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
def safe_label(name: str) -> str:
if not IDENTIFIER_RE.match(name):
raise ValueError(f"invalid Neo4j label: {name!r}")
return name Type guard
import re
_ID = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
def is_valid_identifier(name) -> bool:
return isinstance(name, str) and bool(_ID.match(name)) Try / catch
try:
validate_identifier(label, "label")
except ValueError as e:
logger.error("sanitize label before use: %s", e)
label = re.sub(r"[^A-Za-z0-9_]", "_", label) or "_default"
validate_identifier(label, "label") Prevention
- Derive identifiers from trusted constants, never raw filenames or user input
- Normalize dynamic names with re.sub(r'[^A-Za-z0-9_]', '_', name) and reject empty results
- Validate all label/type/property/index names at config load, before any Cypher is built
- Never accept hyphens or dots in Neo4j identifiers — map them to underscores
When it happens
Trigger: Passing a label, relationship type, property key, or index name containing characters outside [a-zA-Z_][a-zA-Z0-9_]* (spaces, hyphens, dots, backticks, unicode, or an empty string) to any Neo4j connector API that builds Cypher (node/relationship upsert, delete, index create).
Common situations: Label derived from a filename or table name containing hyphens ('my-table'); namespaced property names ('meta.title'); user-supplied labels; empty label after string slicing; non-ASCII labels.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Invalid FalkorDB {kind}: {name!r}. Must match [a-zA-Z_][a-zA
- build_node_upsert requires at least one primary key field
- build_node_delete requires at least one primary key field
- build_relationship_upsert requires PK fields for from, to, a
- build_relationship_delete requires at least one primary key
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/5c475e6a1a41c6b2.
Report an issue: GitHub.