langgenius/dify · error · ValueError

an environment variable cannot be upserted and deleted in th

Error message

an environment variable cannot be upserted and deleted in the same patch

What it means

Raised by SyncEnvironmentVariablePatchPayload.validate_patch when the same id appears in both the upsert list and the delete list (api/controllers/console/app/workflow.py:135-136). Upserting and deleting the same variable in one patch is contradictory and would produce an undefined merge result, so the validator rejects it. Surfaced as a Pydantic ValidationError (400).

Source

Thrown at api/controllers/console/app/workflow.py:136

class SyncEnvironmentVariablePatchPayload(BaseModel):
    environment_variables: list[dict[str, Any]] = Field(default_factory=list)
    deleted_environment_variable_ids: list[str] = Field(default_factory=list)

    @model_validator(mode="after")
    def validate_patch(self) -> Self:
        """Require stable, disjoint IDs so the service can merge the patch deterministically."""
        upsert_ids = [variable.get("id") for variable in self.environment_variables]
        if any(not isinstance(variable_id, str) or not variable_id for variable_id in upsert_ids):
            raise ValueError("patched environment variables require an id")
        if len(set(upsert_ids)) != len(upsert_ids):
            raise ValueError("patched environment variable ids must be unique")
        if any(not variable_id for variable_id in self.deleted_environment_variable_ids):
            raise ValueError("deleted environment variable ids must not be empty")
        if len(set(self.deleted_environment_variable_ids)) != len(self.deleted_environment_variable_ids):
            raise ValueError("deleted environment variable ids must be unique")
        if set(upsert_ids).intersection(self.deleted_environment_variable_ids):
            raise ValueError("an environment variable cannot be upserted and deleted in the same patch")
        return self


class SyncDraftWorkflowPayload(BaseModel):
    model_config = ConfigDict(extra="forbid")

    graph: dict[str, Any]
    features: dict[str, Any]
    hash: str | None = None
    is_collaborative: bool = Field(default=False, alias="_is_collaborative")
    environment_variable_patch: SyncEnvironmentVariablePatchPayload | None = None
    conversation_variables: list[dict[str, Any]] = Field(
        default_factory=list,
    )


class BaseWorkflowRunPayload(BaseModel):
    files: list[dict[str, Any]] | None = Field(default=None)

View on GitHub (pinned to ef8544b173)

Solutions

  1. Reconcile client state: if a variable is marked for deletion, remove it from the upsert list (and vice versa).
  2. Compute the patch deterministically from a single source of truth before submit.
  3. Add a client-side intersection check: `upsertIds.filter(id => !deleteIds.includes(id))`.

Example fix

// before: id 'a' both upserted and deleted
{environment_variables:[{id:'a',...}], deleted_environment_variable_ids:['a']}
// after: pick one — here, keep the upsert, drop from deletes
{environment_variables:[{id:'a',...}], deleted_environment_variable_ids:[]}
Defensive patterns

Strategy: validation

Validate before calling

// Reconcile: a variable cannot be both upserted and deleted
const upsertIds = new Set(payload.environment_variables.map(v => v.id))
payload.deleted_environment_variable_ids = payload.deleted_environment_variable_ids.filter(id => !upsertIds.has(id))

Type guard

function disjoint(upserts, deletes) {
  const u = new Set(upserts.map(v=>v.id)); return deletes.every(id => !u.has(id))
}

Prevention

When it happens

Trigger: A variable id is present in environment_variables (upsert) AND in deleted_environment_variable_ids in the same request. Happens when UI state allows a variable to be both edited and marked for deletion.

Common situations: User edits a variable then (via a stale action) marks it deleted; front-end keeps both a 'pending edit' and a 'pending delete' entry; merging two partial patches without reconciling conflicts; buggy undo flow.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/5478703879994964. Report an issue: GitHub.