{"record":{"id":"c378982b2168a959","repo":"langgenius/dify","slug":"expected-dict-for-file-got-type-raw-value","errorCode":null,"errorMessage":"expected dict for file, got {type(raw_value)}","messagePattern":"expected dict for file, got (.+?)","errorType":"validation","errorClass":"InvalidArgumentError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py","lineNumber":250,"sourceCode":"\n        variable_id_str = str(variable_id)\n        variable = draft_var_srv.get_variable(variable_id=variable_id_str)\n        if variable is None:\n            raise NotFoundError(description=f\"variable not found, id={variable_id_str}\")\n        if variable.app_id != pipeline.id:\n            raise NotFoundError(description=f\"variable not found, id={variable_id_str}\")\n\n        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)","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py#L232-L268","documentation":"Returned (HTTP 400, error_code `invalid_param`) by PATCH on a draft variable whose `value_type` is `SegmentType.FILE`. The handler expects the request body `value` field to be a JSON object (a file mapping with type/transfer_method/url/upload_file_id) so it can be passed to `build_from_mapping`. If `value` is a string, number, list, or null-shaped non-dict, the isinstance check fails and InvalidArgumentError is raised before any DB write.","triggerScenarios":"PATCHing a FILE-typed variable with `\"value\": \"<upload_file_id>\"` (string) instead of an object; sending `\"value\": [...]`; sending a bare upload_file_id string; client serializing the file object to a string before sending.","commonSituations":"Frontend form binding the file picker to a string id instead of the mapping object. Misreading the API doc and assuming value is the upload_file_id. Migration scripts that previously stored file ids as strings.","solutions":["Send `value` as an object matching the file mapping shape: `{\"type\":\"image\",\"transfer_method\":\"local_file\",\"upload_file_id\":\"<id>\"}`.","For remote files use `{\"type\":\"image\",\"transfer_method\":\"remote_url\",\"url\":\"<signed url>\"}`.","Inspect `variable.value_type` from the GET response and only attach a file mapping when it equals `file`.","Add a client-side schema validator (Pydantic/zod) for the file mapping before PATCH."],"exampleFix":"// before\n{ \"value\": \"daded54f-72c7-4f8e-9d18-9b0abdd9f190\" }\n// after\n{\n  \"value\": {\n    \"type\": \"image\",\n    \"transfer_method\": \"local_file\",\n    \"upload_file_id\": \"daded54f-72c7-4f8e-9d18-9b0abdd9f190\"\n  }\n}","handlingStrategy":"type-guard","validationCode":"function asFileMapping(v: unknown): { type: string; transfer_method: string; upload_file_id?: string; url?: string } {\n  if (!v || typeof v !== 'object' || Array.isArray(v)) {\n    throw new Error('file value must be a mapping object')\n  }\n  const o = v as Record<string, unknown>\n  if (typeof o.type !== 'string' || typeof o.transfer_method !== 'string') {\n    throw new Error('file mapping requires string `type` and `transfer_method`')\n  }\n  return o as any\n}\n// before PATCH:\nif (variable.value_type === 'file') rawValue = asFileMapping(rawValue)","typeGuard":"function isFileMapping(v: unknown): v is { type: string; transfer_method: string; upload_file_id?: string; url?: string } {\n  return (\n    !!v &&\n    typeof v === 'object' &&\n    !Array.isArray(v) &&\n    typeof (v as any).type === 'string' &&\n    typeof (v as any).transfer_method === 'string'\n  )\n}","tryCatchPattern":"try {\n  await patch(...)\n} catch (e) {\n  if (e.code === 'invalid_param' && /expected dict for file/.test(e.message)) {\n    // rewrap value as a file mapping and retry once\n  } else throw e\n}","preventionTips":["Bind the file picker to the full mapping object, not the upload_file_id string.","Validate `typeof value === 'object' && !Array.isArray(value)` before PATCH when value_type is `file`.","Document the expected mapping shape next to the form that produces it.","Add a Pydantic/zod schema for the file mapping on the client."],"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"}