Release Breaking Changes#
Catalog of critical backward-incompatible changes in Dify that require operator action during upgrades. Standard flask upgrade-db / docker compose up is not sufficient for these; they require additional manual steps.
model_type Enum Rename — 1.14.x#
Affected versions: Any instance upgrading to 1.14.0 or later with pre-existing model provider records.
What changed#
Dify 1.14.x renamed model_type column values in the dify PostgreSQL database without running a data migration against existing rows :
| Old value | New value |
|---|---|
text-generation | llm |
embeddings | text-embedding |
reranking | rerank |
The code-level rename is tracked in PR #34488 and the bug was closed in PR #36520 .
Symptoms#
- UI shows "Credentials unavailable" on all pre-configured model providers
HTTP 400 "Credential with id … not found"in browser console- Model deletions appear to succeed but the record reappears on refresh
- Inference still works — only management operations (view, edit, delete) are broken
Fix#
Back up the affected tables, then run against the dify database :
UPDATE provider_models SET model_type = 'llm' WHERE model_type = 'text-generation';
UPDATE provider_models SET model_type = 'text-embedding' WHERE model_type = 'embeddings';
UPDATE provider_models SET model_type = 'rerank' WHERE model_type = 'reranking';
UPDATE provider_model_credentials SET model_type = 'llm' WHERE model_type = 'text-generation';
UPDATE provider_model_credentials SET model_type = 'text-embedding' WHERE model_type = 'embeddings';
UPDATE provider_model_credentials SET model_type = 'rerank' WHERE model_type = 'reranking';
UPDATE tenant_default_models SET model_type = 'llm' WHERE model_type = 'text-generation';
UPDATE tenant_default_models SET model_type = 'text-embedding' WHERE model_type = 'embeddings';
UPDATE tenant_default_models SET model_type = 'rerank' WHERE model_type = 'reranking';
UPDATE provider_model_settings SET model_type = 'llm' WHERE model_type = 'text-generation';
UPDATE provider_model_settings SET model_type = 'text-embedding' WHERE model_type = 'embeddings';
UPDATE provider_model_settings SET model_type = 'rerank' WHERE model_type = 'reranking';
UPDATE load_balancing_model_configs SET model_type = 'llm' WHERE model_type = 'text-generation';
UPDATE load_balancing_model_configs SET model_type = 'text-embedding' WHERE model_type = 'embeddings';
UPDATE load_balancing_model_configs SET model_type = 'rerank' WHERE model_type = 'reranking';
tenant_default_models edge case: If the UPDATE throws SQLSTATE 23505 (duplicate key on the (tenant_id, model_type) unique constraint), the new value already exists for that tenant. Delete the stale legacy-valued row first :
DELETE FROM tenant_default_models
WHERE tenant_id = '<id>' AND model_type IN ('text-generation','embeddings','reranking');
After the SQL fix, uninstall and reinstall affected model plugins to ensure plugin state is consistent .
SSRF Proxy Deny-by-Default — 1.15.0#
Affected versions: Upgrades from any version < 1.15.0 where workflows or HTTP-request nodes call non-marketplace URLs.
What changed#
As of 1.15.0, the Squid SSRF proxy enforces a deny-by-default policy . Previously, outbound HTTP requests from workflow nodes passed through with far fewer restrictions. The proxy now blocks:
- All RFC-1918 / private IP ranges (
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16, and more — 14 ranges total) - All destinations except
.marketplace.dify.aiby default
See docker/ssrf_proxy/squid.conf.template for the full ACL list .
Symptoms#
- Workflow HTTP-request nodes return
403 Forbidden - Worker logs show:
Request to URL <url> failed on attempt 1: 403 Forbidden ssrf_proxycontainer logs show:TCP_DENIED/403for the blocked destinationToolSSRFError: Access to '<url>' was blocked by SSRF protectionin stack traces
Fix#
Add exceptions via .env and restart the proxy :
| Variable | Allows |
|---|---|
SSRF_PROXY_ALLOW_PRIVATE_DOMAINS | Named hosts/domains that resolve to private IPs |
SSRF_PROXY_ALLOW_PRIVATE_IPS | Specific private IPs or CIDR ranges |
Example .env additions:
SSRF_PROXY_ALLOW_PRIVATE_DOMAINS=internal.mycompany.com,ollama
SSRF_PROXY_ALLOW_PRIVATE_IPS=10.10.5.42,192.168.1.0/24
Then restart:
docker compose restart ssrf_proxy
# or for full config regeneration:
docker compose down && docker compose up -d
The docker/ssrf_proxy/docker-entrypoint.sh reads these variables at startup and generates Squid ACL fragments into /etc/squid/dify_allow_private.conf, which is evaluated before the deny rules .
Other Known Upgrade Issues#
| Version | Issue | Reference |
|---|---|---|
| 1.13.x → 1.14.x | Duplicate plugin_unique_identifier in dify_plugin.plugins causes daemon restart loop (SQLSTATE 23505) | Issue #38276 |
| 1.15.0+ | Stale plugin version directories on disk cause record not found in daemon logs; run flask backfill-plugin-auto-upgrade | Issue #38268 |
| 1.15.0 | DB_TYPE env var now required; add DB_TYPE=postgresql |
Key References#
| Resource | Notes |
|---|---|
| Issue #36129 | Primary thread for model_type enum mismatch, with full SQL fix |
| Discussion #38100 | SSRF 403 after 1.10.x → 1.15.0 upgrade, with resolution |
squid.conf.template | Full Squid ACL rules |
api/core/helper/ssrf_proxy.py | API-layer SSRF detection and ToolSSRFError |
| Plugin Database Integrity KB | All four plugin DB failure patterns with diagnostics |