langgenius/dify · error · MigrationDataError

{label} JSON must be an object.

Error message

{label} JSON must be an object.

What it means

MigrationDataError raised by _load_json_object when the file parses successfully as JSON but the top-level value is not a dict (object). The migration package and selection formats require a JSON object at the root; arrays, strings, numbers, or null are rejected.

Source

Thrown at api/commands/data_migration.py:275

        )
        with session_factory.create_session() as session:
            result = MigrationExportService().export(selection, session=session)
        MigrationPackageService().save_package(result.package, output_file, overwrite=overwrite)
        click.echo(click.style(f"Output written to {output_file}", fg="green"))
        _print_wizard_step("Report")
        _render_report(result.report_items, context=_with_output_path(result.report_context, output_file))
    except MigrationDataError as exc:
        raise click.ClickException(str(exc)) from exc


def _load_json_object(path: str, label: str) -> dict[str, Any]:
    try:
        with Path(path).open(encoding="utf-8") as file:
            raw = json.load(file)
    except json.JSONDecodeError as exc:
        raise MigrationDataError(f"{label} JSON is invalid: {exc.msg}") from exc
    if not isinstance(raw, dict):
        raise MigrationDataError(f"{label} JSON must be an object.")
    return raw


def _require_options(*options: tuple[str, object | None]) -> None:
    missing_options = [name for name, value in options if value is None]
    if missing_options:
        raise click.UsageError(f"Missing option(s): {', '.join(missing_options)}.")


def _build_options_override(
    package_options: ImportOptions,
    *,
    id_strategy: str | None,
    conflict_strategy: str | None,
    create_app_api_token_on_import: bool | None,
) -> ImportOptions | None:
    if id_strategy is None and conflict_strategy is None and create_app_api_token_on_import is None:
        return None

View on GitHub (pinned to ef8544b173)

Solutions

  1. Open the file and confirm the top-level value is an object literal `{ ... }`.
  2. If it is an array, re-export through MigrationPackageService.save_package which writes the correct object shape.
  3. Check that the CLI option points to the package file, not a sibling report or log.
  4. Validate with: `python -c "import json,sys; print(type(json.load(open(sys.argv[1]))))" file.json` expecting <class 'dict'>.

Example fix

# before - array at root
[ { "apps": {...} } ]

# after
{ "apps": { "ids": [], "all": false } }
Defensive patterns

Strategy: type-guard

Validate before calling

import json
from pathlib import Path

def require_json_object(path: str, label: str) -> dict:
    raw = json.loads(Path(path).read_text(encoding="utf-8"))
    if not isinstance(raw, dict):
        raise SystemExit(f"{label} must be a JSON object, got {type(raw).__name__}")
    return raw

Type guard

def is_json_object(path: str) -> bool:
    try:
        with Path(path).open(encoding="utf-8") as f:
            return isinstance(json.load(f), dict)
    except (json.JSONDecodeError, OSError):
        return False

Try / catch

try:
    raw = _load_json_object(path, "package")
except MigrationDataError as exc:
    click.echo(str(exc), err=True)
    raise click.Abort()

Prevention

When it happens

Trigger: Triggered when the labeled JSON file is valid JSON but is an array (e.g. [{...}]), a bare scalar, or null instead of an object.

Common situations: The user wrapped the package in an array, exported a list of items instead of a keyed object, or pointed the option at the wrong file (e.g. a report file that is an array).

Related errors


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