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 NoneView on GitHub (pinned to ef8544b173)
Solutions
- Open the file and confirm the top-level value is an object literal `{ ... }`.
- If it is an array, re-export through MigrationPackageService.save_package which writes the correct object shape.
- Check that the CLI option points to the package file, not a sibling report or log.
- 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
- Always export via MigrationPackageService.save_package to guarantee the object shape.
- Never hand-edit the package root into an array.
- Point CLI options at the package file, not at report/log arrays.
- Validate the root type before invoking the wizard.
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
- {label} JSON is invalid: {exc.msg}
- No tenants found.
- No workflow or advanced-chat apps found for the selected ten
- usage_invalid_flag
- expected integer, got ${JSON.stringify(raw)}
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/b0ac39d43c48911a.
Report an issue: GitHub.