{"record":{"id":"c72b5dae6cf79cf3","repo":"langgenius/dify","slug":"expected-list-for-files-got-type-raw-value","errorCode":null,"errorMessage":"expected list for files, got {type(raw_value)}","messagePattern":"expected list for files, got (.+?)","errorType":"validation","errorClass":"InvalidArgumentError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py","lineNumber":258,"sourceCode":"        new_name = args.get(self._PATCH_NAME_FIELD, None)\n        raw_value = args.get(self._PATCH_VALUE_FIELD, None)\n        if new_name is None and raw_value is None:\n            return variable\n\n        new_value = None\n        if raw_value is not None:\n            match variable.value_type:\n                case SegmentType.FILE:\n                    if not isinstance(raw_value, dict):\n                        raise InvalidArgumentError(description=f\"expected dict for file, got {type(raw_value)}\")\n                    raw_value = build_from_mapping(\n                        mapping=raw_value,\n                        tenant_id=pipeline.tenant_id,\n                        access_controller=_file_access_controller,\n                    )\n                case SegmentType.ARRAY_FILE:\n                    if not isinstance(raw_value, list):\n                        raise InvalidArgumentError(description=f\"expected list for files, got {type(raw_value)}\")\n                    if len(raw_value) > 0 and not isinstance(raw_value[0], dict):\n                        raise InvalidArgumentError(description=f\"expected dict for files[0], got {type(raw_value)}\")\n                    raw_value = build_from_mappings(\n                        mappings=raw_value,\n                        tenant_id=pipeline.tenant_id,\n                        access_controller=_file_access_controller,\n                    )\n                case _:\n                    pass\n            new_value = build_segment_with_type(variable.value_type, raw_value)\n        draft_var_srv.update_variable(variable, name=new_name, value=new_value)\n        db.session.commit()\n        return variable\n\n    @console_ns.response(204, \"Variable deleted successfully\")\n    @_api_prerequisite\n    def delete(self, _current_user: Account, pipeline: Pipeline, variable_id: UUID):\n        draft_var_srv = WorkflowDraftVariableService(","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py#L240-L276","documentation":"Returned (HTTP 400, `invalid_param`) by PATCH on a draft variable whose `value_type` is `SegmentType.ARRAY_FILE`. The handler requires `value` to be a JSON array (a list of file mappings) so it can be handed to `build_from_mappings`. Sending a dict, string, number, or any non-list raises InvalidArgumentError before the per-element checks run.","triggerScenarios":"PATCHing an ARRAY_FILE variable with a single file object instead of a one-element list; sending `\"value\": { ... }`; sending a comma-separated string of ids.","commonSituations":"UI binding the multi-file picker to the first selected file rather than the array. Backend transforming the array into a single object during normalization. Confusing FILE vs ARRAY_FILE semantics.","solutions":["Send `value` as a list: `[{...file mapping 1...}, {...file mapping 2...}]`.","For zero files send an empty array `[]` rather than omitting the field (note: empty array still satisfies isinstance(list)).","Verify `variable.value_type === 'array[file]'` on the client before constructing the payload.","Double-check the form state returns an array even when one file is selected."],"exampleFix":"// before\n{ \"value\": { \"type\": \"image\", \"transfer_method\": \"local_file\", \"upload_file_id\": \"<id>\" } }\n// after\n{ \"value\": [ { \"type\": \"image\", \"transfer_method\": \"local_file\", \"upload_file_id\": \"<id>\" } ] }","handlingStrategy":"type-guard","validationCode":"function asFileMappings(v: unknown): object[] {\n  if (!Array.isArray(v)) {\n    throw new Error('array[file] value must be a JSON array')\n  }\n  return v\n}\n// before PATCH for value_type === 'array[file]':\nrawValue = asFileMappings(rawValue)","typeGuard":"function isFileArray(v: unknown): v is object[] {\n  return Array.isArray(v)\n}","tryCatchPattern":"try {\n  await patch(...)\n} catch (e) {\n  if (e.code === 'invalid_param' && /expected list for files/.test(e.message)) {\n    // wrap single object into [object] or rebuild array, then retry once\n  } else throw e\n}","preventionTips":["Always build an array (even for a single file) when value_type is `array[file]`.","Distinguish FILE vs ARRAY_FILE in the UI binding.","Add a form-level assertion that the multi-file picker yields an array."],"tags":["rag-pipeline","validation","file-upload","api","rest"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}