{"record":{"id":"e41505354b915900","repo":"pola-rs/polars","slug":"you-need-to-pass-delta-merge-options-with-at-least-e41505","errorCode":null,"errorMessage":"you need to pass delta_merge_options with at least a given predicate for `MERGE` to work.","messagePattern":"you need to pass delta_merge_options with at least a given predicate for `MERGE` to work\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/lazyframe/frame.py","lineNumber":3340,"sourceCode":"        # We aren't calling into polars-native write functions so we just update\n        # the storage_options here.\n        storage_options = (\n            {**(storage_options or {}), **credential_provider_creds}\n            if storage_options is not None or credential_provider_builder is not None\n            else None\n        )\n        stream = self.collect_batches(\n            engine=engine,\n            maintain_order=False,\n            chunk_size=None,\n            lazy=True,\n            optimizations=optimizations,\n        )\n\n        if mode == \"merge\":\n            if delta_merge_options is None:\n                msg = \"you need to pass delta_merge_options with at least a given predicate for `MERGE` to work.\"\n                raise ValueError(msg)\n            if isinstance(target, str):\n                dt = DeltaTable(table_uri=target, storage_options=storage_options)\n            else:\n                dt = target\n\n            return dt.merge(stream, **delta_merge_options)  # type: ignore[arg-type]\n\n        else:\n            if delta_write_options is None:\n                delta_write_options = {}\n\n            write_deltalake(  # pyrefly: ignore[no-matching-overload]\n                table_or_uri=target,\n                data=stream,  # type: ignore[call-overload]\n                mode=mode,\n                storage_options=storage_options,\n                **delta_write_options,\n            )","sourceCodeStart":3322,"sourceCodeEnd":3358,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/lazyframe/frame.py#L3322-L3358","documentation":"sink_delta(mode='merge') performs a Delta Lake MERGE (upsert), which requires merge semantics — at minimum a predicate — supplied via delta_merge_options. Without them polars raises ValueError because it cannot guess how to match source rows to target rows.","triggerScenarios":"lf.sink_delta(target, mode='merge') with no delta_merge_options; passing delta_merge_options=None; confusing mode='merge' with mode='overwrite'/'append' semantics.","commonSituations":"Building incremental upsert pipelines into Delta tables; switching a write job from append to merge without adding merge predicates.","solutions":["Pass delta_merge_options with predicate, e.g. {'predicate': 'target.id = source.id', 'source_alias': 'source', 'target_alias': 'target'} plus insert/update/delete actions as needed","If you only want to replace data, use mode='overwrite' with delta_write_options instead","Verify the predicate references the correct source/target aliases"],"exampleFix":"# before\nlf.sink_delta('delta:/tbl', mode='merge')\n\n# after\nlf.sink_delta(\n    'delta:/tbl', mode='merge',\n    delta_merge_options={\n        'predicate': 'target.id = source.id',\n        'source_alias': 'source',\n        'target_alias': 'target',\n        'when_matched_update_all': True,\n    },\n)","handlingStrategy":"validation","validationCode":"if mode == 'merge' and not delta_merge_options:\n    raise ValueError('mode=merge requires delta_merge_options with a predicate')\nlf.sink_delta(target, mode=mode, delta_merge_options=delta_merge_options)","typeGuard":"def is_valid_merge_options(opts: dict | None) -> bool:\n    return bool(opts) and 'predicate' in opts","tryCatchPattern":null,"preventionTips":["Treat merge as a distinct code path with its own config validation","Test upsert jobs with dry-run predicates against a staging table"],"tags":["polars","delta-lake","merge","sink-delta"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}