{"record":{"id":"afd455fa57ddbe02","repo":"langflow-ai/langflow","slug":"remove-label-cannot-be-combined-with-upsert-for","errorCode":null,"errorMessage":"{remove_label} cannot be combined with upsert for the same id: {conflicts}","messagePattern":"(.+?) cannot be combined with upsert for the same id: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"src/backend/base/langflow/api/v1/mappers/deployments/watsonx_orchestrate/payloads.py","lineNumber":249,"sourceCode":"\n\ndef _validate_api_remove_conflicts(\n    *,\n    remove_ids: list[Any],\n    upsert_operations: list[Any],\n    remove_label: str,\n    upsert_attr_name: str,\n) -> None:\n    normalized_remove_ids = {str(remove_id).strip() for remove_id in remove_ids if str(remove_id).strip()}\n    normalized_upsert_ids = {\n        str(getattr(operation, upsert_attr_name, \"\")).strip()\n        for operation in upsert_operations\n        if str(getattr(operation, upsert_attr_name, \"\")).strip()\n    }\n    conflicts = sorted(normalized_remove_ids.intersection(normalized_upsert_ids))\n    if conflicts:\n        msg = f\"{remove_label} cannot be combined with upsert for the same id: {conflicts}\"\n        raise ValueError(msg)\n\n\ndef _validate_api_unused_raw_app_ids(*, raw_app_ids: set[str], referenced_app_ids: set[str]) -> None:\n    unused_raw_app_ids = sorted(raw_app_ids.difference(referenced_app_ids))\n    if unused_raw_app_ids:\n        msg = f\"connections contains app_id values not referenced by operations: {unused_raw_app_ids}\"\n        raise ValueError(msg)\n\n\ndef _validate_api_unique_connection_app_ids(*, connections: list[WatsonxApiKeyValueConnectionPayload]) -> None:\n    app_id_counts = Counter(connection.app_id for connection in connections)\n    duplicates = sorted(app_id for app_id, count in app_id_counts.items() if count > 1)\n    if duplicates:\n        msg = f\"connections contains duplicate app_id values: {duplicates}\"\n        raise ValueError(msg)\n\n\nclass WatsonxApiDeploymentUpdatePayload(BaseModel):","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/v1/mappers/deployments/watsonx_orchestrate/payloads.py#L231-L267","documentation":"API payload validator: an id that is being removed outright (remove_flows / remove_tools) must not also appear in the upsert lists (upsert_flows[].flow_version_id / upsert_tools[].tool_id). Combining remove with upsert for the same id is contradictory; ValueError '{remove_label} cannot be combined with upsert for the same id' is raised, surfacing as 422.","triggerScenarios":"PATCH a wxO deployment whose provider_data includes remove_flows containing a UUID that also appears as some upsert_flows[].flow_version_id, or remove_tools containing a tool_id also present in upsert_tools.","commonSituations":"UI allowing 'edit and delete' simultaneously; request built by concatenating an update payload and a delete payload; stale state where a client re-upserts a flow it just queued for removal.","solutions":["Pick one action per id: to delete it put it only in remove_flows/remove_tools; to modify it put it only in the upsert list.","Rebuild the payload from a single source of truth (current deployment state + user intent) so each id lands in exactly one bucket.","Validate client-side: assert disjointness between remove ids and upsert ids before sending."],"exampleFix":"// before\n{\"upsert_flows\": [{\"flow_version_id\": \"fv1\", ...}], \"remove_flows\": [\"fv1\"]}\n// after\n{\"remove_flows\": [\"fv1\"]}","handlingStrategy":"validation","validationCode":"upsert_fv = {str(i[\"flow_version_id\"]) for i in provider_data.get(\"upsert_flows\", [])}\nremove_fv = {str(fv) for fv in provider_data.get(\"remove_flows\", [])}\nassert upsert_fv.isdisjoint(remove_fv)\nupsert_t = {i[\"tool_id\"] for i in provider_data.get(\"upsert_tools\", [])}\nremove_t = set(provider_data.get(\"remove_tools\", []))\nassert upsert_t.isdisjoint(remove_t)","typeGuard":"def no_remove_upsert_conflict(provider_data: dict) -> bool:\n    for upsert_key, remove_key, id_attr in ((\"upsert_flows\", \"remove_flows\", \"flow_version_id\"), (\"upsert_tools\", \"remove_tools\", \"tool_id\")):\n        upsert_ids = {str(i.get(id_attr)) for i in provider_data.get(upsert_key, [])}\n        if upsert_ids & {str(r) for r in provider_data.get(remove_key, [])}:\n            return False\n    return True","tryCatchPattern":null,"preventionTips":["Build each id's intent exactly once from current state + user action.","Assert remove lists are disjoint from upsert lists before sending.","Do not concatenate independent update and delete payloads."],"tags":["watsonx-orchestrate","pydantic","validation","http-422","conflict","upsert"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}