cocoindex-io/cocoindex · error · ValueError

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 performs three MERGEs: source node, target node, then the relationship. Each MERGE needs its own key clause, so all three PK lists (from_pk_fields, to_pk_fields, rel_pk_fields) must be non-empty; any empty one makes the pattern invalid and raises ValueError.

Source

Thrown at python/cocoindex/connectors/neo4j/_cypher.py:135

    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:
        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)}) "
        f"MERGE (t:{_quote(to_label)} {_key_clause('to_key', to_pk_fields)}) "
        f"MERGE (s)-[r:{_quote(rel_type)} {_key_clause('rel_key', rel_pk_fields)}]->(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

  1. Declare primary key fields for the relationship record itself in addition to both endpoint tables
  2. Verify both from/to node specs have non-empty primary keys
  3. Fail fast in your schema-loading code if any of the three PK lists is empty

Example fix

// before
build_relationship_upsert(rel_type="MENTIONS", from_label="Doc", to_label="Doc", from_pk_fields=["id"], to_pk_fields=["id"], rel_pk_fields=[])
// after
build_relationship_upsert(rel_type="MENTIONS", from_label="Doc", to_label="Doc", from_pk_fields=["id"], to_pk_fields=["id"], rel_pk_fields=["ordinal"])
Defensive patterns

Strategy: validation

Validate before calling

assert from_pk_fields and to_pk_fields and rel_pk_fields, (
    "relationship upsert needs PK fields for both endpoints and the relationship"
)
build_relationship_upsert(rel_type=rel_type, from_label=from_label, to_label=to_label,
    from_pk_fields=from_pk_fields, to_pk_fields=to_pk_fields, rel_pk_fields=rel_pk_fields)

Type guard

def all_have_pks(from_pk, to_pk, rel_pk) -> bool:
    return bool(from_pk) and bool(to_pk) and bool(rel_pk)

Try / catch

try:
    cypher = build_relationship_upsert(...)
except ValueError as e:
    if "requires PK fields" in str(e):
        logger.error("relationship %r or an endpoint lacks a primary key", rel_type)
        raise
    raise

Prevention

When it happens

Trigger: Calling build_relationship_upsert with an empty from_pk_fields, to_pk_fields, or rel_pk_fields — e.g. the relationship record, one of its endpoint tables, or the relationship itself has no primary key fields declared.

Common situations: Relationship rows defined without a PK (assuming endpoints alone identify them); endpoint node tables defined without primary keys; dynamic schemas where one endpoint's PK list was computed as 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


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/8707f34b395da902. Report an issue: GitHub.