Environment
- Dify: 1.13.0
- Dify Plugin SDK (dify_plugin): 0.7.2
Summary
When a plugin uploads a file via self.session.file.upload() and then invokes a chatflow app with that file as an input
using transfer_method: "local_file" + upload_file_id: , the chatflow invocation fails with HTTP 400:
{"code":"invalid_param","message":"Invalid upload file","status":400}
Expected behavior
A file uploaded through the Plugin SDK (session.file.upload()) should be usable as a chatflow app file input with
transfer_method: "local_file" by passing its returned id as upload_file_id.
Actual behavior
Chatflow invocation fails with:
- HTTP 400
- invalid_param
- "Invalid upload file"
Root cause (confirmed)
Chatflow local_file parsing validates against upload_files (scoped by tenant_id), but session.file.upload()
stores the uploaded file in tool_files and returns an id that exists only in tool_files.
Evidence (SQL)
Replace <APP_ID> and <UPLOAD_ID> with your values.
-- App tenant id
SELECT id, tenant_id, name
FROM apps
WHERE id = '<APP_ID>';
-- Fails: no row in upload_files for the upload id
SELECT id, tenant_id, name, extension, mime_type, size, source_url
FROM upload_files
WHERE id = '<UPLOAD_ID>';
-- Succeeds: row exists in tool_files for the same upload id + tenant
SELECT id, tenant_id, name, file_key, mimetype, size, original_url
FROM tool_files
WHERE id = '<UPLOAD_ID>';
Minimal reproduction (plugin code)
This reproduces the failure by uploading a DOCX via session.file.upload() and then sending the returned id as a
local_file chat input.
from dify_plugin import Endpoint
from werkzeug import Request, Response
class Repro(Endpoint):
def _invoke(self, r: Request, values, settings) -> Response:
app_id = settings["app"]["app_id"]
content = b"PK\x03\x04..." # any bytes; use a real docx in actual repro
upload = self.session.file.upload(
filename="repro.docx",
content=content,
mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
inputs = {
"realfiles": [
{
"transfer_method": "local_file",
"upload_file_id": upload.id,
"type": "document",
}
]
}
# This invocation fails with 400: {"message":"Invalid upload file"}
result = self.session.app.chat.invoke(
app_id=app_id,
query="test",
inputs=inputs,
response_mode="blocking",
conversation_id=None,
)
return Response(response=str(result), mimetype="text/plain")
Environment
Summary
When a plugin uploads a file via self.session.file.upload() and then invokes a chatflow app with that file as an input
using transfer_method: "local_file" + upload_file_id: , the chatflow invocation fails with HTTP 400:
{"code":"invalid_param","message":"Invalid upload file","status":400}
Expected behavior
A file uploaded through the Plugin SDK (session.file.upload()) should be usable as a chatflow app file input with
transfer_method: "local_file" by passing its returned id as upload_file_id.
Actual behavior
Chatflow invocation fails with:
Root cause (confirmed)
Chatflow local_file parsing validates against upload_files (scoped by tenant_id), but session.file.upload()
stores the uploaded file in tool_files and returns an id that exists only in tool_files.
Evidence (SQL)
Replace <APP_ID> and <UPLOAD_ID> with your values.
Minimal reproduction (plugin code)
This reproduces the failure by uploading a DOCX via session.file.upload() and then sending the returned id as a
local_file chat input.