{"record":{"id":"57a953afc1c00611","repo":"langflow-ai/langflow","slug":"bundle-bundle-r-is-not-a-valid-bundle-name-lo","errorCode":null,"errorMessage":"--bundle {bundle!r} is not a valid bundle name (lowercase snake_case, 2-64 chars, starts with a letter).  This script ports an in-tree provider folder named exactly the same.","messagePattern":"--bundle (.+?) is not a valid bundle name \\(lowercase snake_case, 2-64 chars, starts with a letter\\)\\.  This script ports an in-tree provider folder named exactly the same\\.","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"scripts/migrate/port_bundle.py","lineNumber":675,"sourceCode":"    text = BASE_PYPROJECT.read_text(encoding=\"utf-8\")\n    return bool(re.search(rf\"^{re.escape(bundle)}\\s*=\\s*\\[\", text, re.MULTILINE))\n\n\ndef _validate_candidate(\n    bundle: str,\n    *,\n    display_name: str | None,\n    migration_release: str | None,\n    discover_consumers: bool,\n) -> PortPlan:\n    \"\"\"Refuse early if the candidate is not eligible for the mechanical port.\"\"\"\n    if not re.fullmatch(r\"[a-z][a-z0-9_]{1,63}\", bundle):\n        msg = (\n            f\"--bundle {bundle!r} is not a valid bundle name (lowercase \"\n            \"snake_case, 2-64 chars, starts with a letter).  This script \"\n            \"ports an in-tree provider folder named exactly the same.\"\n        )\n        raise SystemExit(msg)\n\n    in_tree = LFX_COMPONENTS / bundle\n    if not in_tree.is_dir():\n        msg = f\"In-tree provider directory not found: {in_tree}\"\n        raise SystemExit(msg)\n\n    bundle_dir = BUNDLES_DIR / bundle\n    if bundle_dir.exists():\n        msg = f\"Bundle directory already exists: {bundle_dir}.  Refusing to overwrite.\"\n        raise SystemExit(msg)\n\n    deactivated_dup = LFX_COMPONENTS / \"deactivated\" / bundle\n    if deactivated_dup.is_dir():\n        msg = (\n            f\"A deactivated duplicate exists at {deactivated_dup}.  Resolve \"\n            \"the duplicate manually before porting -- see \"\n            \"src/bundles/PORTING.md § 0.\"\n        )","sourceCodeStart":657,"sourceCodeEnd":693,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/scripts/migrate/port_bundle.py#L657-L693","documentation":"The first eligibility guard in port_bundle.py's planning phase: the --bundle argument must fullmatch the regex [a-z][a-z0-9_]{1,63} — lowercase snake_case, 2-64 characters, starting with a letter — because the script only ports an in-tree provider folder named exactly the same, and the name becomes a Python package name and ext:<slug> id. Anything else (hyphens, uppercase, digits first, too long) aborts with SystemExit before any work is done.","triggerScenarios":"Running port_bundle.py with `--bundle open-ai`, `--bundle OpenAI`, `--bundle 1bundle`, or a 65+ character name. The name is also required to match an existing in-tree directory, so any stylistic deviation is rejected up front.","commonSituations":"Translating a display name ('Open AI') or PyPI distribution name ('lfx-open-ai') into the bundle argument instead of the directory name; assuming kebab-case is accepted because other tooling in the repo uses it.","solutions":["Use the exact in-tree provider directory name: `ls src/lfx/src/lfx/components` (LFX_COMPONENTS) and pass that folder name verbatim.","Convert kebab/display names to snake_case: 'open-ai' -> 'openai', 'My Provider' -> 'my_provider'.","If the provider genuinely needs a different bundle name, rename the in-tree directory first (with all import updates), then port."],"exampleFix":"# before\npython scripts/migrate/port_bundle.py --bundle open-ai\n# error: --bundle 'open-ai' is not a valid bundle name ...\n\n# after\npython scripts/migrate/port_bundle.py --bundle openai","handlingStrategy":"validation","validationCode":"import re\n\ndef valid_bundle_name(name: str) -> bool:\n    return re.fullmatch(r\"[a-z][a-z0-9_]{1,63}\", name) is not None\n\nassert valid_bundle_name(bundle), \"--bundle must be lowercase snake_case, 2-64 chars, letter-first\"","typeGuard":"import re\n\ndef is_bundle_name(name: str) -> bool:\n    \"\"\"Matches port_bundle.py's eligibility regex exactly.\"\"\"\n    return bool(re.fullmatch(r\"[a-z][a-z0-9_]{1,63}\", name))","tryCatchPattern":null,"preventionTips":["Always pass the in-tree directory name verbatim; convert kebab/display names to snake_case first.","The name doubles as the Python package name and ext:<slug> id — keep it stable once published."],"tags":["cli","validation","naming","migration"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}