{"record":{"id":"0fda75e4d281da34","repo":"langgenius/dify","slug":"patched-environment-variable-ids-must-be-unique","errorCode":null,"errorMessage":"patched environment variable ids must be unique","messagePattern":"patched environment variable ids must be unique","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/app/workflow.py","lineNumber":130,"sourceCode":"    value_type: str\n    id: NotRequired[str]\n    name: NotRequired[str]\n    value: NotRequired[Any]\n    description: NotRequired[str | None]\n\n\nclass SyncEnvironmentVariablePatchPayload(BaseModel):\n    environment_variables: list[dict[str, Any]] = Field(default_factory=list)\n    deleted_environment_variable_ids: list[str] = Field(default_factory=list)\n\n    @model_validator(mode=\"after\")\n    def validate_patch(self) -> Self:\n        \"\"\"Require stable, disjoint IDs so the service can merge the patch deterministically.\"\"\"\n        upsert_ids = [variable.get(\"id\") for variable in self.environment_variables]\n        if any(not isinstance(variable_id, str) or not variable_id for variable_id in upsert_ids):\n            raise ValueError(\"patched environment variables require an id\")\n        if len(set(upsert_ids)) != len(upsert_ids):\n            raise ValueError(\"patched environment variable ids must be unique\")\n        if any(not variable_id for variable_id in self.deleted_environment_variable_ids):\n            raise ValueError(\"deleted environment variable ids must not be empty\")\n        if len(set(self.deleted_environment_variable_ids)) != len(self.deleted_environment_variable_ids):\n            raise ValueError(\"deleted environment variable ids must be unique\")\n        if set(upsert_ids).intersection(self.deleted_environment_variable_ids):\n            raise ValueError(\"an environment variable cannot be upserted and deleted in the same patch\")\n        return self\n\n\nclass SyncDraftWorkflowPayload(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n\n    graph: dict[str, Any]\n    features: dict[str, Any]\n    hash: str | None = None\n    is_collaborative: bool = Field(default=False, alias=\"_is_collaborative\")\n    environment_variable_patch: SyncEnvironmentVariablePatchPayload | None = None\n    conversation_variables: list[dict[str, Any]] = Field(","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/app/workflow.py#L112-L148","documentation":"Raised by SyncEnvironmentVariablePatchPayload.validate_patch when the upsert list contains two variables with the same 'id' (api/controllers/console/app/workflow.py:129-130). The merge logic keys on id, so duplicate ids would silently overwrite one another; validation rejects the patch up front. Surfaced as a Pydantic ValidationError (400).","triggerScenarios":"Two entries in environment_variable_patch.environment_variables share the same 'id' value. Common when copy-pasting variable definitions or when a front-end bug double-adds an entry.","commonSituations":"Copy-paste of a variable block without regenerating the id; front-end state desync adding the same variable twice; import payload with colliding ids; test fixture reuse without id rotation.","solutions":["Ensure each upserted variable has a unique id — deduplicate the list before sending.","If editing one variable, include it once; do not re-send the same id twice.","Add a client-side uniqueness check on ids before submit."],"exampleFix":"// before: duplicate id\n[{id:'a', name:'X', value:'1'}, {id:'a', name:'Y', value:'2'}]\n// after: unique ids\n[{id:crypto.randomUUID(), name:'X', value:'1'}, {id:crypto.randomUUID(), name:'Y', value:'2'}]","handlingStrategy":"validation","validationCode":"// Deduplicate upsert ids before submit\nconst seen = new Set()\npayload.environment_variables = payload.environment_variables.filter(v =>\n  seen.has(v.id) ? false : (seen.add(v.id), true))","typeGuard":"function uniqueIds(vars) {\n  const ids = vars.map(v => v.id)\n  return new Set(ids).size === ids.length\n}","tryCatchPattern":null,"preventionTips":["Track variables by id in a Map/Set so duplicates cannot accumulate.","Dedupe before submit."],"tags":["workflow","environment-variables","validation","duplicate","api"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}