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
- Pass exactly three dot-separated numbers: `--migration-release 1.10.0`.
- Strip any 'v' prefix or pre-release suffix from the value you copied.
- 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
- Strip 'v' prefixes and pre-release suffixes before passing --migration-release.
- Copy the exact convention from existing entries in migration_table.json.
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
- provider directory not found: {src}
- --bundle {bundle!r} is not a valid bundle name (lowercase sn
- In-tree provider directory not found: {in_tree}
- A deactivated duplicate exists at {deactivated_dup}. Resolv
- No ``*.py`` files under {in_tree}; nothing to port.
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/52e88ff9c1476ec8.
Report an issue: GitHub.