{"record":{"id":"5147785a0b34e844","repo":"cocoindex-io/cocoindex","slug":"build-node-delete-requires-at-least-one-primary-ke","errorCode":null,"errorMessage":"build_node_delete requires at least one primary key field","messagePattern":"build_node_delete requires at least one primary key field","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"python/cocoindex/connectors/falkordb/_cypher.py","lineNumber":91,"sourceCode":"    write; otherwise the MERGE alone suffices.\n    \"\"\"\n    if not pk_fields:\n        raise ValueError(\"build_node_upsert requires at least one primary key field\")\n    cypher = f\"MERGE (n:{_quote(label)} {_key_clause('key', pk_fields, 'n')})\"\n    if has_value_fields:\n        cypher += \" SET n += $props\"\n    return cypher\n\n\ndef build_node_delete(label: str, pk_fields: Sequence[str]) -> str:\n    \"\"\"``MATCH (n:`Label` {pk: $key_0, ...}) DETACH DELETE n``.\n\n    DETACH DELETE removes any incident edges as a safety measure for nodes that\n    are also referenced as relationship endpoints — without it, the DELETE\n    would fail on nodes that still have edges.\n    \"\"\"\n    if not pk_fields:\n        raise ValueError(\"build_node_delete requires at least one primary key field\")\n    return (\n        f\"MATCH (n:{_quote(label)} {_key_clause('key', pk_fields, 'n')}) \"\n        f\"DETACH DELETE n\"\n    )\n\n\ndef build_relationship_upsert(\n    rel_type: str,\n    from_label: str,\n    from_pk_fields: Sequence[str],\n    to_label: str,\n    to_pk_fields: Sequence[str],\n    rel_pk_fields: Sequence[str],\n    has_value_fields: bool,\n) -> str:\n    \"\"\"Three MERGEs: source endpoint, target endpoint, then the relationship.\n\n    Endpoint properties are NOT touched — they are owned by their table's own","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/connectors/falkordb/_cypher.py#L73-L109","documentation":"build_node_delete() refuses to build a MATCH ... DETACH DELETE query when pk_fields is empty. Identifying which node to delete requires at least one key property; with none, the query would either delete all nodes or be invalid, so the library raises instead.","triggerScenarios":"Calling build_node_delete(label, pk_fields=[]) — deleting nodes for a table/handler whose primary key field list is empty.","commonSituations":"Same root cause as the upsert case: schema defined without a primary key; reconciler cleanup path for a mis-declared table; pk fields list built dynamically and ended up empty.","solutions":["Add at least one primary key field to the node table schema and pass it as pk_fields.","Inspect how pk_fields is constructed upstream (config parsing, field filtering) to see why it is empty.","Use the same key fields for delete as for upsert so matched nodes are consistent."],"exampleFix":"// before\nbuild_node_delete(label=\"Person\", pk_fields=[])\n// after\nbuild_node_delete(label=\"Person\", pk_fields=[\"person_id\"])","handlingStrategy":"validation","validationCode":"if not pk_fields:\n    raise ValueError('node table needs at least one primary key field before delete')\ncypher = build_node_delete(label, pk_fields)","typeGuard":"def has_pk_fields(pk_fields: object) -> bool:\n    return isinstance(pk_fields, (list, tuple)) and len(pk_fields) > 0","tryCatchPattern":"try:\n    cypher = build_node_delete(label, pk_fields)\nexcept ValueError as e:\n    logger.error('cannot build node delete: %s', e)\n    raise ConfigError(f'table {label!r} has no primary key to match for delete') from e","preventionTips":["Reuse the same pk_fields list for upsert and delete paths so they cannot diverge.","Fail schema validation at startup if a node table has no key fields, before any reconcile runs.","Log the table schema once at configuration time to catch missing keys early."],"tags":["cypher","falkordb","primary-key","delete"],"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"}