langflow-ai/langflow · error · ValueError
{label} add_app_ids and remove_app_ids must not overlap: {ov
Error message
{label} add_app_ids and remove_app_ids must not overlap: {overlap} What it means
API payload validator: within each upsert_flows / upsert_tools operation, add_app_ids and remove_app_ids must be disjoint. An id appearing in both lists is contradictory (bind and unbind in one operation), so ValueError '{label} add_app_ids and remove_app_ids must not overlap' is raised and FastAPI returns 422.
Source
Thrown at src/backend/base/langflow/api/v1/mappers/deployments/watsonx_orchestrate/payloads.py:230
def _validate_api_remove_not_raw(*, operations: list[Any], raw_app_ids: set[str], attr_name: str, label: str) -> None:
for operation in operations:
remove_app_ids = getattr(operation, attr_name, None)
if not remove_app_ids:
continue
invalid_raw = sorted(raw_app_ids.intersection(set(remove_app_ids)))
if invalid_raw:
msg = f"{label} must not reference connections app_ids: {invalid_raw}"
raise ValueError(msg)
def _validate_api_add_remove_overlap(*, operations: list[Any], label: str) -> None:
for operation in operations:
add_app_ids = set(getattr(operation, "add_app_ids", []) or [])
remove_app_ids = set(getattr(operation, "remove_app_ids", []) or [])
overlap = sorted(add_app_ids.intersection(remove_app_ids))
if overlap:
msg = f"{label} add_app_ids and remove_app_ids must not overlap: {overlap}"
raise ValueError(msg)
def _validate_api_remove_conflicts(
*,
remove_ids: list[Any],
upsert_operations: list[Any],
remove_label: str,
upsert_attr_name: str,
) -> None:
normalized_remove_ids = {str(remove_id).strip() for remove_id in remove_ids if str(remove_id).strip()}
normalized_upsert_ids = {
str(getattr(operation, upsert_attr_name, "")).strip()
for operation in upsert_operations
if str(getattr(operation, upsert_attr_name, "")).strip()
}
conflicts = sorted(normalized_remove_ids.intersection(normalized_upsert_ids))
if conflicts:
msg = f"{remove_label} cannot be combined with upsert for the same id: {conflicts}"View on GitHub (pinned to 976ec789d2)
Solutions
- Decide the final state per app_id: it goes in add_app_ids or remove_app_ids, never both.
- Fix client diff logic: last-write-wins per id, then partition into add/remove sets.
- Validate before send: assert set(add).isdisjoint(set(remove)) for every operation item.
Example fix
// before
{"tool_id": "t1", "add_app_ids": ["a", "b"], "remove_app_ids": ["b"]}
// after
{"tool_id": "t1", "add_app_ids": ["a", "b"], "remove_app_ids": []} Defensive patterns
Strategy: validation
Validate before calling
for item in operations:
add, remove = set(item.get("add_app_ids", [])), set(item.get("remove_app_ids", []))
assert add.isdisjoint(remove), f"overlap: {add & remove}" Type guard
const addRemoveDisjoint = (item: {add_app_ids?: string[]; remove_app_ids?: string[]}): boolean => {
const add = new Set(item.add_app_ids ?? []);
return (item.remove_app_ids ?? []).every(id => !add.has(id));
}; Prevention
- Partition desired end-state per app_id into add OR remove, never both.
- Run a disjointness assertion in the request builder.
- Watch for UI toggle races that enqueue both an add and a remove for the same id.
When it happens
Trigger: PATCH a wxO deployment with an upsert_flows or upsert_tools item whose add_app_ids and remove_app_ids arrays share at least one app_id.
Common situations: UI toggle race producing both add and remove for the same app; naive diff code that keeps an id in remove when the user re-adds it; merging two partial operation payloads into one item.
Related errors
- {label} must not reference connections app_ids: {invalid_raw
- String must not be empty.
- credentials contains duplicate key values: {sorted(duplicate
- {remove_label} cannot be combined with upsert for the same i
- connections contains app_id values not referenced by operati
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/5660feec1cdb7116.
Report an issue: GitHub.