cocoindex-io/cocoindex · error
build_relationship_upsert requires PK fields for from, to, a
Error message
build_relationship_upsert requires PK fields for from, to, and the relationship
What it means
build_relationship_upsert() requires non-empty primary key fields for the source node, the target node, AND the relationship itself, because it emits three MERGE clauses — one per endpoint and one for the relationship — each of which needs key properties to match on. If any of the three lists is empty it raises instead of producing a query that would match non-deterministically.
Source
Thrown at python/cocoindex/connectors/falkordb/_cypher.py:113
)
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:
raise ValueError(
"build_relationship_upsert requires PK fields for from, to, and the relationship"
)
cypher = (
f"MERGE (s:{_quote(from_label)} {_key_clause('from_key', from_pk_fields, 's')}) "
f"MERGE (t:{_quote(to_label)} {_key_clause('to_key', to_pk_fields, 't')}) "
f"MERGE (s)-[r:{_quote(rel_type)} {_key_clause('rel_key', rel_pk_fields, 'r')}]->(t)"
)
if has_value_fields:
cypher += " SET r += $props"
return cypher
def build_relationship_delete(rel_type: str, pk_fields: Sequence[str]) -> str:
"""``MATCH ()-[r:`RelType` {pk: $key_0, ...}]->() DELETE r``.
Endpoints are intentionally not deleted — they're tracked by their own
table handlers and will be deleted by their own reconciler if orphaned.
"""View on GitHub (pinned to e84aa99b32)
Solutions
- Provide PK fields for both endpoints (the same keys their node tables use) and at least one PK field for the relationship (add an id/hash property if it has no natural key).
- Verify the relationship mapping's key_fields configuration covers from, to, and rel entries.
- Check upstream field filtering that may strip the key columns.
Example fix
// before build_relationship_upsert(from_label="Person", from_pk_fields=[], to_label="Company", to_pk_fields=["id"], rel_type="WORKS_AT", rel_pk_fields=[]) // after build_relationship_upsert(from_label="Person", from_pk_fields=["person_id"], to_label="Company", to_pk_fields=["id"], rel_type="WORKS_AT", rel_pk_fields=["since"])
Defensive patterns
Strategy: validation
Validate before calling
for name, fields in (('from', from_pk_fields), ('to', to_pk_fields), ('rel', rel_pk_fields)):
if not fields:
raise ValueError(f'relationship needs PK fields for {name}')
cypher = build_relationship_upsert(from_label, from_pk_fields, to_label, to_pk_fields, rel_type, rel_pk_fields, ...) Type guard
def all_have_keys(*field_lists: object) -> bool:
return all(isinstance(f, (list, tuple)) and len(f) > 0 for f in field_lists) Try / catch
try:
cypher = build_relationship_upsert(...)
except ValueError as e:
logger.error('relationship upsert misconfigured: %s', e)
raise ConfigError(f'relationship {rel_type!r} must define PKs for endpoints and itself') from e Prevention
- Define the relationship's own primary key even if it feels optional — add an id or hash property.
- Mirror endpoint PKs from the node table definitions instead of duplicating them by hand.
- Validate the full relationship mapping (labels + three key lists) when it is loaded, not at query build time.
When it happens
Trigger: Calling build_relationship_upsert with from_pk_fields=[], to_pk_fields=[], or rel_pk_fields=[] — e.g. a relationship mapping that defines endpoint labels but no key fields, or a relationship with no properties so the author passed an empty rel key list.
Common situations: Relationship schema omitted the relationship's own primary key assuming it wasn't needed; endpoint node tables declared without PKs; refactoring renamed key fields and the lists ended up empty.
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_node_delete requires at least one primary key field
- build_relationship_delete requires at least one primary key
- build_relationship_index_create requires at least one field
- build_relationship_index_drop requires at least one field
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/2aca4693cd7e8eff.
Report an issue: GitHub.