{"record":{"id":"452ff3aab9b94fef","repo":"cocoindex-io/cocoindex","slug":"build-relationship-delete-requires-at-least-one-pr-452ff3","errorCode":null,"errorMessage":"build_relationship_delete requires at least one primary key field","messagePattern":"build_relationship_delete requires at least one primary key field","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/connectors/neo4j/_cypher.py","lineNumber":155,"sourceCode":"        )\n    cypher = (\n        f\"MERGE (s:{_quote(from_label)} {_key_clause('from_key', from_pk_fields)}) \"\n        f\"MERGE (t:{_quote(to_label)} {_key_clause('to_key', to_pk_fields)}) \"\n        f\"MERGE (s)-[r:{_quote(rel_type)} {_key_clause('rel_key', rel_pk_fields)}]->(t)\"\n    )\n    if has_value_fields:\n        cypher += \" SET r += $props\"\n    return cypher\n\n\ndef build_relationship_delete(rel_type: str, pk_fields: Sequence[str]) -> str:\n    \"\"\"``MATCH ()-[r:`RelType` {pk: $key_0, ...}]->() DELETE r``.\n\n    Endpoints are intentionally not deleted — they're tracked by their own\n    table handlers and will be deleted by their own reconciler if orphaned.\n    \"\"\"\n    if not pk_fields:\n        raise ValueError(\n            \"build_relationship_delete requires at least one primary key field\"\n        )\n    return (\n        f\"MATCH ()-[r:{_quote(rel_type)} {_key_clause('key', pk_fields)}]->() DELETE r\"\n    )\n\n\ndef build_node_index_create(\n    name: str,\n    label: str,\n    fields: Sequence[str],\n) -> str:\n    \"\"\"``CREATE INDEX <name> IF NOT EXISTS FOR (n:`Label`) ON (n.`f1`, n.`f2`, ...)``.\n\n    Neo4j requires named indexes; ``IF NOT EXISTS`` makes setup idempotent.\n    \"\"\"\n    if not fields:\n        raise ValueError(\"build_node_index_create requires at least one field\")","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/connectors/neo4j/_cypher.py#L137-L173","documentation":"build_relationship_delete generates MATCH ()-[r:RelType {pk: ...}]->() DELETE r, requiring at least one key property to identify the relationship. An empty pk_fields list would produce invalid Cypher, so it raises ValueError.","triggerScenarios":"Calling build_relationship_delete with pk_fields=[] — the relationship spec has no primary key fields when deletions are reconciled.","commonSituations":"Relationship declared without its own primary key; PK fields list built dynamically and left empty; refactors that removed the relationship's key column.","solutions":["Declare primary key fields on the relationship spec (same ones used in build_relationship_upsert)","Pass the identical rel_pk_fields to both the upsert and delete builders so deletes can find rows the upserts created","Add an early assertion that the relationship PK list is non-empty at schema construction"],"exampleFix":"// before\nbuild_relationship_delete(rel_type=\"MENTIONS\", pk_fields=[])\n// after\nbuild_relationship_delete(rel_type=\"MENTIONS\", pk_fields=[\"ordinal\"])","handlingStrategy":"validation","validationCode":"if not rel_pk_fields:\n    raise ValueError(\"relationship delete needs the same PK fields used for upsert\")\ncypher = build_relationship_delete(rel_type=rel_type, pk_fields=rel_pk_fields)","typeGuard":"def pk_ok(fields) -> bool:\n    return bool(fields)","tryCatchPattern":"try:\n    cypher = build_relationship_delete(rel_type, pk_fields)\nexcept ValueError as e:\n    if \"at least one primary key\" in str(e):\n        logger.error(\"cannot reconcile deletes for %r: no relationship PK\", rel_type)\n        raise\n    raise","preventionTips":["Share one constant/spec for relationship PK fields across upsert and delete","Declare an explicit relationship key column (e.g. ordinal or composite key) in the schema","Unit-test delete generation for every relationship spec"],"tags":["neo4j","cypher","primary-key","relationship"],"backgroundTag":"missing-required-argument","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}