langgenius/dify · error · MigrationDataError

{label} JSON is invalid: {exc.msg}

Error message

{label} JSON is invalid: {exc.msg}

What it means

MigrationDataError raised by _load_json_object when json.load on the given path raises JSONDecodeError. The label (e.g. 'package manifest', 'selection') identifies which input file failed, and exc.msg carries the parser's position/message. The wizard's outer handler converts MigrationDataError into a click.ClickException for CLI display.

Source

Thrown at api/commands/data_migration.py:273

            conflict_strategy=conflict_strategy,
            output_file=output_file,
        )
        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:

View on GitHub (pinned to ef8544b173)

Solutions

  1. Run the file through a JSON linter (e.g. `python -m json.tool path/to/file.json`) to locate the syntax error.
  2. Re-export or re-download the migration package to rule out truncation.
  3. Ensure the file is UTF-8 encoded without a leading BOM.
  4. Use the label in the message to identify which CLI argument's file is broken.

Example fix

# before - file has trailing comma
{
  "source_tenant": {...},
}

# after
{
  "source_tenant": {}
}
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

def validate_json_file(path: str) -> dict:
    text = Path(path).read_text(encoding="utf-8")
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        raise SystemExit(f"{path} is not valid JSON; fix with a linter first")

Type guard

def is_valid_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:
    payload = _load_json_object(path, "package")
except MigrationDataError as exc:
    click.echo(f"Input file rejected: {exc}", err=True)
    raise click.Abort()

Prevention

When it happens

Trigger: Triggered when the --package/--selection (or other labeled) JSON file passed to the data-migration wizard is syntactically invalid: trailing commas, unquoted keys, truncated download, BOM/encoding issues, or empty file.

Common situations: The migration package file was partially downloaded or corrupted, edited by hand with a syntax error, or saved with a non-UTF-8 encoding. The label tells you which file to re-inspect.

Related errors


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