{"record":{"id":"fda060df24283c3f","repo":"langgenius/dify","slug":"unsupported-media-type","errorCode":null,"errorMessage":"Unsupported Media Type","messagePattern":"Unsupported Media Type","errorType":"http","errorClass":null,"httpStatus":415,"severity":"error","filePath":"api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py","lineNumber":234,"sourceCode":"    @rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.DATASET_EDIT)\n    @console_ns.expect(console_ns.models[DraftWorkflowSyncPayload.__name__])\n    @console_ns.response(200, \"Success\", console_ns.models[RagPipelineWorkflowSyncResponse.__name__])\n    def post(self, current_user: Account, pipeline: Pipeline):\n        \"\"\"\n        Sync draft workflow\n        \"\"\"\n        content_type = request.headers.get(\"Content-Type\", \"\")\n\n        if \"application/json\" in content_type:\n            payload_dict = console_ns.payload or {}\n            payload = DraftWorkflowSyncPayload.model_validate(payload_dict)\n        elif \"text/plain\" in content_type:\n            try:\n                payload = DraftWorkflowSyncPayload.model_validate_json(request.data)\n            except (ValueError, ValidationError):\n                return {\"message\": \"Invalid JSON data\"}, 400\n        else:\n            abort(415)\n        rag_pipeline_service = RagPipelineService(db.session())\n\n        try:\n            environment_variables_list = Workflow.normalize_environment_variable_mappings(\n                payload.environment_variables or [],\n            )\n            environment_variables = [\n                variable_factory.build_environment_variable_from_mapping(obj) for obj in environment_variables_list\n            ]\n            conversation_variables_list = payload.conversation_variables or []\n            conversation_variables = [\n                variable_factory.build_conversation_variable_from_mapping(obj) for obj in conversation_variables_list\n            ]\n            workflow = rag_pipeline_service.sync_draft_workflow(\n                pipeline=pipeline,\n                graph=payload.graph,\n                unique_hash=payload.hash,\n                account=current_user,","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py#L216-L252","documentation":"HTTP 415 Unsupported Media Type from the RAG pipeline draft-workflow sync endpoint. The handler inspects `request.headers[\"Content-Type\"]`: only `application/json` (parsed via `console_ns.payload`) and `text/plain` (parsed via `model_validate_json(request.data)`) are accepted; any other Content-Type triggers `abort(415)` with the default message.","triggerScenarios":"POST/PUT to the draft-workflow sync route with a Content-Type other than `application/json` or `text/plain` — e.g. `multipart/form-data`, `application/x-www-form-urlencoded`, missing Content-Type, or a Content-Type with an unexpected main type.","commonSituations":"Client using `FormData` instead of JSON; curl/Postman defaulting to `application/x-www-form-urlencoded`; SDK that auto-sets `multipart/form-data` for large payloads; reverse proxy stripping Content-Type.","solutions":["Set `Content-Type: application/json` and send a JSON body matching `DraftWorkflowSyncPayload`.","If streaming a raw JSON string, use `Content-Type: text/plain` and ensure the body is valid JSON parseable by `DraftWorkflowSyncPayload.model_validate_json`.","Do not use `FormData`/`multipart` for this route — it expects a single JSON document.","Verify no intermediary (gateway, SDK) rewrites the Content-Type."],"exampleFix":"// before\nfetch(url, { method: 'PUT', body: new FormData(form) })\n// after\nfetch(url, {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify(payload)\n})","handlingStrategy":"validation","validationCode":"def ok_content_type(ct: str | None) -> bool:\n    ct = (ct or \"\").lower()\n    return \"application/json\" in ct or \"text/plain\" in ct\n\nheaders = {\"Content-Type\": \"application/json\"}\nassert ok_content_type(headers[\"Content-Type\"])","typeGuard":"ALLOWED = (\"application/json\", \"text/plain\")\ndef is_allowed_content_type(ct: str | None) -> bool:\n    return any(a in (ct or \"\").lower() for a in ALLOWED)","tryCatchPattern":"try:\n    resp = client.put(url, json=payload, headers={\"Content-Type\": \"application/json\"})\nexcept HTTPError as err:\n    if err.response.status_code == 415:\n        # switch to JSON body and retry\n        ...\n    elif err.response.status_code == 400:\n        # text/plain body failed model_validate_json\n        ...\n    raise","preventionTips":["Always set Content-Type: application/json for this route.","Do not use FormData/multipart here.","Verify no gateway rewrites Content-Type.","For text/plain, ensure the body is valid JSON parseable by DraftWorkflowSyncPayload."],"tags":["content-type","http-415","rag-pipeline","workflow-sync"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}