langgenius/dify · error · MigrationDataError

No tenants found.

Error message

No tenants found.

What it means

MigrationDataError raised by _prompt_source_tenant when the database query for tenants returns zero rows. The export wizard cannot proceed without a source tenant to select, so it aborts before prompting. This is surfaced via click.ClickException by the wizard's outer handler.

Source

Thrown at api/commands/data_migration.py:310

    if id_strategy is None and conflict_strategy is None and create_app_api_token_on_import is None:
        return None
    return ImportOptions.from_mapping(
        {
            "id_strategy": id_strategy or package_options.id_strategy,
            "conflict_strategy": conflict_strategy or package_options.conflict_strategy,
            "create_app_api_token_on_import": (
                create_app_api_token_on_import
                if create_app_api_token_on_import is not None
                else package_options.create_app_api_token_on_import
            ),
        }
    )


def _prompt_source_tenant() -> Tenant:
    tenants = list(db.session.scalars(sa.select(Tenant).order_by(Tenant.name.asc())).all())
    if not tenants:
        raise MigrationDataError("No tenants found.")

    _print_wizard_step("Source Tenant")
    click.echo("Source tenants:")
    for index, tenant in enumerate(tenants, 1):
        click.echo(f"{index}. {tenant.name} ({tenant.id})")

    tenant_index = click.prompt("Select one source tenant by number", type=int, default=1, show_default=True)
    if tenant_index < 1 or tenant_index > len(tenants):
        raise click.ClickException(f"Selection index out of range: {tenant_index}")
    return tenants[tenant_index - 1]


def _eligible_apps_for_tenant(tenant_id: str) -> list[App]:
    return list(
        db.session.scalars(
            sa.select(App)
            .where(App.tenant_id == tenant_id, App.mode.in_(SUPPORTED_WIZARD_APP_MODES))
            .order_by(App.name.asc())

View on GitHub (pinned to ef8544b173)

Solutions

  1. Verify DATABASE_URI points at the intended Dify database and that the tenants table has rows.
  2. Run `SELECT count(*) FROM tenants;` to confirm data exists.
  3. If the DB is genuinely empty, initialize the workspace first through normal onboarding before migrating.
  4. Ensure the API worker running the command has the same DB config as the running instance.

Example fix

# before - pointing at empty DB
DATABASE_URI=postgresql://user:pass@host/empty_db

# after
DATABASE_URI=postgresql://user:pass@host/dify_prod
Defensive patterns

Strategy: validation

Validate before calling

from sqlalchemy import select
from models import Tenant

def tenant_count(session) -> int:
    return session.scalar(select(func.count()).select_from(Tenant)) or 0

# preflight
if tenant_count(session) == 0:
    raise SystemExit("No tenants in DB; initialize the workspace first")

Type guard

def has_tenants(session) -> bool:
    return session.scalars(select(Tenant).limit(1)).first() is not None

Try / catch

try:
    tenant = _prompt_source_tenant()
except MigrationDataError as exc:
    click.echo(str(exc), err=True)
    raise click.Abort()

Prevention

When it happens

Trigger: Triggered when `sa.select(Tenant)` against the configured database returns an empty list - i.e. the Dify instance has no tenants initialized.

Common situations: Running the migration wizard against a fresh/uninitialized database, against the wrong database (wrong DATABASE_URI), or against a schema where the tenants table is empty (e.g. a test fixture DB).

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/e1344eba73303d91. Report an issue: GitHub.