{"id":"e16c22131cd3823e","repo":"sqlalchemy/sqlalchemy","slug":"set-parameter-dictionary-must-not-be-empty-e16c22","errorCode":null,"errorMessage":"set parameter dictionary must not be empty","messagePattern":"set parameter dictionary must not be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/sqlalchemy/dialects/sqlite/dml.py","lineNumber":260,"sourceCode":"        (\"update_values_to_set\", InternalTraversal.dp_dml_values),\n        (\"update_whereclause\", InternalTraversal.dp_clauseelement),\n    ]\n\n    def __init__(\n        self,\n        index_elements: _OnConflictIndexElementsT = None,\n        index_where: _OnConflictIndexWhereT = None,\n        set_: _OnConflictSetT = None,\n        where: _OnConflictWhereT = None,\n    ):\n        super().__init__(\n            index_elements=index_elements,\n            index_where=index_where,\n        )\n\n        if isinstance(set_, dict):\n            if not set_:\n                raise ValueError(\"set parameter dictionary must not be empty\")\n        elif isinstance(set_, ColumnCollection):\n            set_ = dict(set_)\n        else:\n            raise ValueError(\n                \"set parameter must be a non-empty dictionary \"\n                \"or a ColumnCollection such as the `.c.` collection \"\n                \"of a Table object\"\n            )\n        self.update_values_to_set = {\n            coercions.expect(roles.DMLColumnRole, k): coercions.expect(\n                roles.ExpressionElementRole, v, type_=NULLTYPE, is_crud=True\n            )\n            for k, v in set_.items()\n        }\n        self.update_whereclause = (\n            coercions.expect(roles.WhereHavingRole, where)\n            if where is not None\n            else None","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/sqlalchemy/sqlalchemy/blob/d9b44cb731bd27e6e82c99a3c0fb598214e8e7ff/lib/sqlalchemy/dialects/sqlite/dml.py#L242-L278","documentation":"Raised by OnConflictDoUpdate.__init__ when the set_ parameter is a dict but is empty. The SQLite ON CONFLICT DO UPDATE clause requires at least one column to update, so an empty assignment set is rejected with ValueError at statement construction. This is the dedicated, more specific error for the empty-dict case before the general type-validation error in the elif/else branch.","triggerScenarios":"Calling sqlite.insert(table).on_conflict_do_update(set_={}) or building the set_ dict dynamically such that it ends up empty (e.g. all keys filtered out, a comprehension that yields nothing, or a dict built from an empty config).","commonSituations":"Dynamically constructing the SET clause from a request payload or diff that contains no fields. Conditional update builders that filter to zero columns. Copy-paste templates left with a placeholder empty dict. Logic that computes changed columns and finds none.","solutions":["Ensure the set_ dict has at least one entry; if it may be empty, skip emitting on_conflict_do_update entirely (use on_conflict_do_nothing or no conflict clause).","Guard before building the statement: `if updates: stmt = stmt.on_conflict_do_update(set_=updates) else: stmt = stmt.on_conflict_do_nothing()`.","Default to a meaningful column such as the updated_at timestamp when no other fields change.","Validate upstream input so empty payloads are rejected before reaching the DB layer."],"exampleFix":"// before\nstmt = sqlite_insert(User).values(id=1, name=\"x\").on_conflict_do_update(\n    index_elements=[User.id],\n    set_={},  # raises ValueError\n)\n\n// after\nupdates = {\"name\": \"x\"}  # computed upstream\nconflict_clause = (\n    sqlite_insert(User).values(id=1, name=\"x\")\n    .on_conflict_do_update(index_elements=[User.id], set_=updates)\n    if updates\n    else sqlite_insert(User).values(id=1, name=\"x\").on_conflict_do_nothing(index_elements=[User.id])\n)","handlingStrategy":"validation","validationCode":"# before building insert(t).on_conflict_do_update(set_=set_):\nif isinstance(set_, dict) and not set_:\n    raise ValueError(\"on_conflict_do_update(set_=...) dict must not be empty\")\n# optionally rebuild from non-None values, or skip the upsert if nothing remains:\nset_ = {k: v for k, v in set_.items() if v is not None}\nif not set_:\n    stmt = insert(t).on_conflict_do_nothing()  # nothing to update","typeGuard":"from sqlalchemy.sql.base import ColumnCollection\n\ndef is_valid_on_conflict_set(s) -> bool:\n    return (isinstance(s, ColumnCollection)\n            or (isinstance(s, dict) and len(s) > 0))","tryCatchPattern":"try:\n    stmt = insert(t).on_conflict_do_update(set_=set_)\nexcept ValueError as e:\n    if \"must not be empty\" in str(e):\n        set_ = {k: v for k, v in set_.items() if v is not None}\n        stmt = (insert(t).on_conflict_do_update(set_=set_)\n                if set_ else insert(t).on_conflict_do_nothing())","preventionTips":["Ensure the dict passed to on_conflict_do_update(set_=...) contains at least one key.","Filter out None/empty values before building set_; if it ends up empty, use on_conflict_do_nothing() instead.","For whole-row updates pass the table's .c ColumnCollection rather than a dict."],"tags":["sqlalchemy","sqlite","upsert","on-conflict","valueerror"],"analyzedSha":"d9b44cb731bd27e6e82c99a3c0fb598214e8e7ff","analyzedAt":"2026-08-01T17:20:52.075Z","schemaVersion":2}