langflow-ai/langflow · error · SystemExit

--migration-release {migration_release!r} must be a three-se

Error message

--migration-release {migration_release!r} must be a three-segment SemVer (e.g. 1.10.0).

What it means

port_bundle.py accepts an optional --migration-release flag used to tag the migration mapping entries with the release in which the component moves out of tree. The value must be an exact three-segment SemVer (\d+\.\d+\.\d+); anything else — '1.10', 'v1.10.0', '1.10.0-rc1', 'latest' — fails the fullmatch and aborts with this message before planning.

Source

Thrown at scripts/migrate/port_bundle.py:739

        text = src.read_text(encoding="utf-8")
        if "from langflow" in text:
            msg = (
                f"{src} imports from ``langflow`` -- the bundle is installed "
                "against the public BUNDLE_API surface (lfx), not Langflow "
                "internals.  Either rewrite the import or leave the component "
                "in-tree.  See src/bundles/PORTING.md § 0."
            )
            raise SystemExit(msg)
        classes = () if src.name == "__init__.py" else _discover_classes(text)
        component_files.append(ComponentFile(path=src, classes=classes))

    if not component_files and not nested_subdirs:
        msg = f"No ``*.py`` files under {in_tree}; nothing to port."
        raise SystemExit(msg)

    if migration_release is not None and not re.fullmatch(r"\d+\.\d+\.\d+", migration_release):
        msg = f"--migration-release {migration_release!r} must be a three-segment SemVer (e.g. 1.10.0)."
        raise SystemExit(msg)

    resolved_display = display_name if display_name is not None else bundle.replace("_", " ").title()

    shared_base = LFX_BASE / bundle
    shared_base_dir = shared_base if shared_base.is_dir() else None

    backend_test_dirs = _discover_backend_test_dirs(bundle)
    ruff_ignores = _discover_ruff_ignores(bundle)
    base_extra_present = _discover_base_extra(bundle)

    legacy_dynamic, legacy_all = _parse_legacy_init(in_tree / "__init__.py")

    consumer_rewrites: tuple[ConsumerRewrite, ...] = ()
    if discover_consumers:
        consumer_rewrites = _discover_external_consumers(bundle, in_tree, bundle_dir, shared_base_dir=shared_base_dir)

    return PortPlan(
        bundle=bundle,

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Pass exactly three dot-separated numbers: `--migration-release 1.10.0`.
  2. Strip any 'v' prefix or pre-release suffix from the value you copied.
  3. If unsure which release to record, check recent migration entries in migration_table.json for the convention.

Example fix

# before
python scripts/migrate/port_bundle.py --bundle openai --migration-release v1.10.0-rc1
# error: --migration-release 'v1.10.0-rc1' must be a three-segment SemVer (e.g. 1.10.0).

# after
python scripts/migrate/port_bundle.py --bundle openai --migration-release 1.10.0
Defensive patterns

Strategy: validation

Validate before calling

import re

def valid_migration_release(value: str) -> bool:
    return re.fullmatch(r"\d+\.\d+\.\d+", value) is not None

Type guard

import re

def is_three_segment_semver(value: str) -> bool:
    return bool(re.fullmatch(r"\d+\.\d+\.\d+", value))

Prevention

When it happens

Trigger: Running `python scripts/migrate/port_bundle.py --bundle <name> --migration-release 1.10` or any non-three-numeric-segment value. Pre-release suffixes are rejected because the field feeds tooling that compares plain SemVer triples.

Common situations: Passing a two-segment minor version; copying a tag like 'v1.10.0' with the leading 'v'; pasting a full dev version from pyproject (e.g. 1.10.0.dev3).

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/52e88ff9c1476ec8. Report an issue: GitHub.