cocoindex-io/cocoindex · error · ValueError
build_node_delete requires at least one primary key field
Error message
build_node_delete requires at least one primary key field
What it means
build_node_delete generates MATCH (n:Label {pk: ...}) DETACH DELETE n, which needs at least one key property to locate the node. Empty pk_fields would yield a malformed match pattern, so it raises ValueError.
Source
Thrown at python/cocoindex/connectors/neo4j/_cypher.py:116
pattern in MERGE just fine.
"""
if not pk_fields:
raise ValueError("build_node_upsert requires at least one primary key field")
cypher = f"MERGE (n:{_quote(label)} {_key_clause('key', pk_fields)})"
if has_value_fields:
cypher += " SET n += $props"
return cypher
def build_node_delete(label: str, pk_fields: Sequence[str]) -> str:
"""``MATCH (n:`Label` {pk: $key_0, ...}) DETACH DELETE n``.
DETACH DELETE removes any incident edges as a safety measure for nodes
that are also referenced as relationship endpoints — without it, the
DELETE would fail on nodes that still have edges.
"""
if not pk_fields:
raise ValueError("build_node_delete requires at least one primary key field")
return f"MATCH (n:{_quote(label)} {_key_clause('key', pk_fields)}) DETACH DELETE n"
def build_relationship_upsert(
rel_type: str,
from_label: str,
from_pk_fields: Sequence[str],
to_label: str,
to_pk_fields: Sequence[str],
rel_pk_fields: Sequence[str],
has_value_fields: bool,
) -> str:
"""Three MERGEs: source endpoint, target endpoint, then the relationship.
Endpoint properties are NOT touched — they are owned by their table's own
record handler. We only ``SET r += $props`` on the relationship itself.
"""
if not from_pk_fields or not to_pk_fields or not rel_pk_fields:View on GitHub (pinned to e84aa99b32)
Solutions
- Ensure the node's table spec declares a primary key so delete handlers receive pk fields
- Pass the same PK fields used for the upsert to build_node_delete
- Guard callers to refuse empty PK lists with a clear message before query construction
Example fix
// before build_node_delete(label="Person", pk_fields=[]) // after build_node_delete(label="Person", pk_fields=["id"])
Defensive patterns
Strategy: validation
Validate before calling
if not pk_fields:
raise ValueError("cannot build node delete without primary key fields")
cypher = build_node_delete(label=label, pk_fields=pk_fields) Type guard
def non_empty_strs(seq) -> bool:
return isinstance(seq, (list, tuple)) and len(seq) > 0 and all(isinstance(x, str) for x in seq) Try / catch
try:
cypher = build_node_delete(label, pk_fields)
except ValueError as e:
if "at least one primary key" in str(e):
logger.error("node %r has no PK; deletes would be unmatchable", label)
raise
raise Prevention
- Reuse the same PK field list for upsert and delete builders
- Assert non-empty primary_key on every node spec at schema construction time
- Include PK coverage in schema tests for all node tables
When it happens
Trigger: Calling build_node_delete with pk_fields=[] — the node spec has no primary key fields when generating the delete statement.
Common situations: Same root cause as the upsert variant: schema declared without a primary key, or an empty field list computed dynamically before deletion reconciliation.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- build_node_upsert 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
- build_node_index_create requires at least one field
- build_node_upsert requires at least one primary key field
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/a85bb0efc8534ce2.
Report an issue: GitHub.