{"record":{"id":"e79d97e259d34217","repo":"langgenius/dify","slug":"deleted-environment-variable-ids-must-not-be-empty","errorCode":null,"errorMessage":"deleted environment variable ids must not be empty","messagePattern":"deleted environment variable ids must not be empty","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/app/workflow.py","lineNumber":132,"sourceCode":"    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(\n        default_factory=list,\n    )","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/app/workflow.py#L114-L150","documentation":"Raised by SyncEnvironmentVariablePatchPayload.validate_patch when deleted_environment_variable_ids contains an empty or otherwise falsy entry (api/controllers/console/app/workflow.py:131-132). Every id in the delete list must be a non-empty string so the service can target a concrete row. Empty strings (or nulls coerced from malformed input) are rejected. Surfaced as a Pydantic ValidationError (400).","triggerScenarios":"Sending deleted_environment_variable_ids like ['', 'abc'] or [null]. Happens when the front-end pushes an empty string placeholder or includes a null slot from a form.","commonSituations":"Front-end bug appending an empty input value; serialized form with a blank row; refactoring that left a default '' in the array; client not filtering blanks before submit.","solutions":["Filter out empty/falsy ids from deleted_environment_variable_ids before submitting: `ids.filter(Boolean)`.","Ensure the front-end only pushes a deletion id once the user confirms a real variable.","Add a client-side check that all entries are non-empty strings."],"exampleFix":"// before: blank entry included\ndeleted_environment_variable_ids: ['', 'abc']\n// after: filter blanks\ndeleted_environment_variable_ids: ['', 'abc'].filter(Boolean)","handlingStrategy":"validation","validationCode":"// Drop falsy ids from the delete list before submit\npayload.deleted_environment_variable_ids = payload.deleted_environment_variable_ids.filter(id => typeof id === 'string' && id.length > 0)","typeGuard":"function cleanDeletes(ids) {\n  return Array.isArray(ids) && ids.every(id => typeof id === 'string' && id.length > 0)\n}","tryCatchPattern":null,"preventionTips":["Filter blank ids from form state before serializing.","Use a controlled input that rejects empty values."],"tags":["workflow","environment-variables","validation","api"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}