Documentsdify
Plugin Database Integrity
Plugin Database Integrity
Type
Topic
Status
Published
Created
Jul 13, 2026
Updated
Jul 13, 2026

Plugin Database Integrity#

Plugin database integrity covers a class of state inconsistencies in Dify's two-database architecture — the main dify PostgreSQL database (managed by Flask-Migrate/Alembic) and the plugin-daemon's separate dify_plugin database (managed by the Go-based plugin daemon). Upgrade sequences between Dify API and the plugin daemon versions are the most common trigger.

Four distinct failure patterns are documented in the wild:

FailureSymptomTrigger
Version reconciliationrecord not found / Plugin table missing in daemon logsOld plugin version directories remain on disk after upgrade
model_type enum mismatch"Credentials unavailable"; models visible but uneditable1.14.x changed enum values without migrating existing rows
Duplicate plugin_unique_identifierSQLSTATE 23505 — daemon crashes in a restart loopDuplicate rows block unique index creation on upgrade
Daemon schema init sequenceTables not created in dify_plugin on fresh installDaemon version skipped a required baseline migration

Pattern 1 — Version Reconciliation (1.15.0+)#

After upgrading to 1.15.0, the plugin daemon scans PLUGIN_INSTALLED_PATH on startup and finds stale directories from pre-upgrade plugin versions. Because the upgrade created new database records with new version identifiers, the old directories no longer have matching rows.

Symptom (INFO-level, not errors):

record not found — SELECT * FROM "plugins" WHERE plugin_unique_identifier = 'langgenius/siliconflow:0.0.12@...'
Plugin table missing for existing PLUGIN_INSTALLED_PATH

Resolution:

  1. Confirm the API migration completed: docker compose exec api flask upgrade-db
  2. Run the backfill: docker compose exec api flask backfill-plugin-auto-upgrade
  3. Verify the plugin daemon is version 0.6.3-local (shipped with 1.15.0)
  4. Remove stale version directories from PLUGIN_INSTALLED_PATH once plugins are confirmed working in the UI

The backfill-plugin-auto-upgrade command (api/commands/plugin.py) backfills category-scoped auto-upgrade strategies and normalizes plugin lists — it must be run after the schema migration completes.


Pattern 2 — model_type Enum Mismatch (1.14.x)#

Dify 1.14.x renamed model_type column values (text-generationllm, embeddingstext-embedding, rerankingrerank) without running a data migration on existing rows. Pre-upgrade records retain legacy values, breaking credential lookups in five tables. The fix is tracked in PR #34488 and closed in PR #36520.

Symptom: Credentials show as unavailable; HTTP 400 "Credential with id ... not found" in browser console; plugin daemon logs record not found for the plugin's plugin_unique_identifier. Models still execute inference (the call path is unaffected) but cannot be viewed, edited, or deleted in the UI.

Manual SQL fix — run against the dify database (back up tables first):

-- Repeat for each of: provider_models, provider_model_credentials,
-- tenant_default_models, provider_model_settings, load_balancing_model_configs
UPDATE <table> SET model_type = 'llm' WHERE model_type = 'text-generation';
UPDATE <table> SET model_type = 'text-embedding' WHERE model_type = 'embeddings';
UPDATE <table> SET model_type = 'rerank' WHERE model_type = 'reranking';

tenant_default_models edge case: The unique constraint on (tenant_id, model_type) may cause SQLSTATE 23505 during the UPDATE if both old and new values already exist. Delete the 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 plugins to ensure plugin state is consistent.


Pattern 3 — Duplicate plugin_unique_identifier (1.13.x → 1.14.x)#

When upgrading from 1.13.x to 1.14.x, the plugin daemon's Go migration attempts to create a unique index on plugins.plugin_unique_identifier. If the table contains duplicate values, PostgreSQL rejects the operation with SQLSTATE 23505 and the daemon restarts in a loop.

Diagnosis:

SELECT plugin_unique_identifier, COUNT(*) AS cnt
  FROM plugins
  GROUP BY plugin_unique_identifier
  HAVING COUNT(*) > 1
  ORDER BY cnt DESC, plugin_unique_identifier;

Remove the extra rows (keep the most recent), then restart the daemon. The migration will succeed once no duplicates exist.

Note: The plugins table is in the dify_plugin database, not the main dify database. Plugin daemon issues should be reported to langgenius/dify-plugins.


Pattern 4 — Plugin Daemon Schema Init Sequence#

Installing directly with plugin-daemon:0.5.6-local fails to create tables in dify_plugin. The workaround is to install with 0.5.3-local first (which correctly initializes the schema), then upgrade to 0.5.6-local. This indicates migration path dependencies in the Go-managed schema.


Key Files and References#

ResourceRole
api/commands/plugin.pybackfill-plugin-auto-upgrade CLI command
api/commands/system.pyflask upgrade-db — runs Alembic migrations
Issue #38268 / commentVersion reconciliation after 1.15.0 upgrade
Issue #36129 model_type enum mismatch; full SQL fix
Issue #38276Duplicate plugin_unique_identifier; diagnostic query
Discussion #37917Plugin daemon table init sequence dependency
PR #34488 / PR #36520Application-level fix for model_type enum