Workflow File Handling#
Overview#
Workflow file handling in Dify converts raw file payload dictionaries (called mappings) into graphon.File objects that flow through the variable pool between nodes. The entire subsystem lives under api/factories/file_factory/. The two public entry points are:
build_from_mapping()β builds a singleFilefrom one mapping dictbuild_from_mappings()β builds a sequence ofFileobjects, enforcingFileUploadConfigcount limits
Both require a tenant_id, a FileAccessControllerProtocol (for tenancy-scoped DB queries), and an optional FileUploadConfig.
The graphon.File Model#
File is defined in the external graphon package (not in the dify repo) and imported as from graphon.file import File, FileTransferMethod, FileType, FileUploadConfig . Key fields constructed by the factory:
| Field | Source |
|---|---|
filename, extension, mime_type, size | Derived from DB record or remote probe |
file_type | Resolved via _resolve_file_type() / standardize_file_type() |
transfer_method | Directly from the mapping's transfer_method key |
remote_url | Source URL or signed URL |
reference | Opaque dify-file-ref:β¦ string (see below) |
storage_key | DB record's storage key (for local/tool/datasource methods) |
Transfer Methods#
build_from_mapping() dispatches on transfer_method to one of four private builders :
1. local_file β _build_from_local_file()#
Looks up an UploadFile DB row by upload_file_id (with fallback to reference / related_id via resolve_mapping_file_id()). Enforces UploadFile.tenant_id == tenant_id . Returns a File with remote_url set to a signed URL via helpers.get_signed_file_url() .
2. remote_url β _build_from_remote_url()#
Two sub-paths:
- Pre-uploaded: if
upload_file_idis present, resolves anUploadFilerow (same as local_file, but emits a signed URL). - Bare URL: calls
get_remote_file_info(url)to HTTP HEAD-probe the URL for MIME type, filename, and file size, then constructs theFilewithremote_urlset to the external URL and noreferenceorstorage_key. See Remote File Handling and Validation for the full probing pipeline.
3. tool_file β _build_from_tool_file()#
Resolves a ToolFile DB row by tool_file_id. Extension is derived from filename, then MIME type, then the file's storage key . Tool files bypass upload config validation entirely (see Config Validation below).
4. datasource_file β _build_from_datasource_file()#
Resolves an UploadFile row by datasource_file_id. Extension is derived from the storage key suffix . Sets both remote_url and url from source_url.
Type Resolution#
After DB/remote lookup, every builder calls _resolve_file_type() which reconciles:
- The
FileTypedetected from extension+MIME viastandardize_file_type() - The caller-supplied
typefield in the mapping
Rules :
- No
typespecified β detected type wins type == "custom"βFileType.CUSTOMalways (extension enforcement deferred to config validation)strict_type_validation=Trueand types differ βValueError("Detected file type does not match the specified type.")
strict_type_validation is True for Service API calls (invoke_from == InvokeFrom.SERVICE_API).
File Reference Format#
Persisted files (local, tool, datasource) get a reference field containing an opaque dify-file-ref:<base64url-json> string built by build_file_reference(). The JSON payload contains record_id (the DB UUID) and optionally storage_key. This format is required by new agent-v2 output contracts; legacy raw record IDs are still readable via the permissive parse_file_reference() helper.
Config Validation#
After the File is built, build_from_mapping() calls is_file_valid_with_config() if a FileUploadConfig is provided. Checks (in order):
TOOL_FILEtransfer method β always validinput_file_typemust be inconfig.allowed_file_types(orCUSTOMmust be in the list)- For
CUSTOM-bucket files:config.allowed_file_extensionswhitelist is enforced (empty list = deny all) - Transfer method must be in
image_config.transfer_methods(images) orconfig.allowed_file_upload_methods(others)
build_from_mappings() additionally enforces config.image_config.number_limits and config.number_limits across the full set .
Mapping TypedDicts#
The factory defines four typed mapping shapes :
| Mapping type | Discriminator | Primary ID key |
|---|---|---|
LocalFileMapping | "local_file" | upload_file_id |
RemoteUrlMapping | "remote_url" | upload_file_id or url/remote_url |
ToolFileMapping | "tool_file" | tool_file_id |
DatasourceFileMapping | "datasource_file" | datasource_file_id |
All support reference (priority 2) and related_id (priority 3, legacy) as fallback ID fields. An id field, if present, maps directly to File.file_id (the graph-layer identity, not the DB record ID).
Callers#
| Call site | Purpose |
|---|---|
workflow_entry.py | Normalizes user-supplied file inputs before injecting into the variable pool |
base_app_generator.py | App-layer file input normalization with upload config |
DifyFileReferenceFactory in node_runtime.py | Protocol-conformant wrapper used by individual workflow nodes |
agent/message_transformer.py | Converts tool file messages during agent node execution |
agent_v2/output_adapter.py | Restores canonical file mappings from agent backend output |
message_files.py | Replay path: converts MessageFile DB rows to File values (skips re-validation) |