cocoindex-io/cocoindex · error · ValueError
row_count must be positive
Error message
row_count must be positive
What it means
`_delete_sql` builds a DELETE statement with one bind placeholder per primary-key row to remove, so it requires `row_count >= 1`. A non-positive count would produce malformed SQL (`IN ()`) and is rejected with this ValueError. It is an internal invariant checked before SQL generation.
Source
Thrown at python/cocoindex/connectors/snowflake/_target.py:300
f"ON {on_clause}",
]
if non_pk_cols:
update_list = ", ".join(f'"{c}" = source."{c}"' for c in non_pk_cols)
sql_parts.append(f"WHEN MATCHED THEN UPDATE SET {update_list}")
sql_parts.append(
f"WHEN NOT MATCHED THEN INSERT ({insert_cols}) VALUES ({insert_values})"
)
return " ".join(sql_parts)
def _delete_sql(
qualified_table_name: str, table_schema: TableSchema[Any], *, row_count: int
) -> str:
pk_cols = table_schema.primary_key
if row_count <= 0:
raise ValueError("row_count must be positive")
if len(pk_cols) == 1:
markers = ", ".join("%s" for _ in range(row_count))
return f'DELETE FROM {qualified_table_name} WHERE "{pk_cols[0]}" IN ({markers})'
row_clauses = []
for _ in range(row_count):
and_clause = " AND ".join(f'"{pk}" = %s' for pk in pk_cols)
row_clauses.append(f"({and_clause})")
return f"DELETE FROM {qualified_table_name} WHERE {' OR '.join(row_clauses)}"
def _encode_value(col: ColumnDef, value: Any) -> Any:
if value is None:
return None
if col.use_parse_json:
if isinstance(value, str):
return valueView on GitHub (pinned to e84aa99b32)
Solutions
- Ensure the caller skips invoking the delete path when the list of keys to delete is empty (guard with `if keys:`).
- If you maintain patched code, filter out empty delete batches before calling _apply_actions.
- Report upstream if this arises from the library's own action computation, since callers normally never pass row_count <= 0.
Example fix
// before
await _apply_actions(conn, actions) # actions.deletes may be empty
// after
if actions.delete_keys:
await _apply_actions(conn, actions) Defensive patterns
Strategy: validation
Validate before calling
if delete_keys:
apply_actions(..., delete_keys=delete_keys) # skip empty batches Try / catch
try:
apply_actions(...)
except ValueError as e:
if "row_count must be positive" in str(e):
logging.warning("Empty delete batch reached SQL builder; skipping")
else:
raise Prevention
- Filter empty delete batches before invoking the apply/sync path.
- Treat this error as an internal bug: capture the action list and report upstream.
- Keep connector code unpatched so action keys are built by the library itself.
When it happens
Trigger: The sync engine calls `_apply_actions` → `_delete_sql` with an empty deletion set (row_count 0 or negative), typically when computing actions without filtering empty delete batches.
Common situations: A run where no rows were deleted but the delete action path was still invoked — usually indicates an internal bug or a custom/patched caller passing an empty key list.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Unexpected column subkey format: {sub_key!r}, expected to st
- change fingerprint {fp} is present in a cached memo entry bu
- row_count must be positive
- Invalid identifier: {name}
- Invalid table name format: {table}
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/d1fc5aea565087cb.
Report an issue: GitHub.