{"record":{"id":"a503ddafed6e1aab","repo":"langflow-ai/langflow","slug":"nested-imports-from-langflow-the-bundle-i","errorCode":null,"errorMessage":"{nested} 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.","messagePattern":"(.+?) 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\\.","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"scripts/migrate/port_bundle.py","lineNumber":716,"sourceCode":"    for src in sorted(in_tree.iterdir()):\n        if src.is_dir():\n            if src.name == \"__pycache__\":\n                continue\n            # Nested subpackage (e.g. ``agentics/helpers``); validate\n            # its files for ``from langflow`` imports too, then plan\n            # to move the whole subdir wholesale.\n            for nested in src.rglob(\"*.py\"):\n                if \"__pycache__\" in nested.parts:\n                    continue\n                text = nested.read_text(encoding=\"utf-8\")\n                if \"from langflow\" in text:\n                    msg = (\n                        f\"{nested} imports from ``langflow`` -- the bundle is \"\n                        \"installed against the public BUNDLE_API surface (lfx), \"\n                        \"not Langflow internals.  Either rewrite the import or \"\n                        \"leave the component in-tree.  See src/bundles/PORTING.md § 0.\"\n                    )\n                    raise SystemExit(msg)\n            nested_subdirs.append(src)\n            continue\n        if not src.is_file() or src.suffix != \".py\":\n            continue\n        text = src.read_text(encoding=\"utf-8\")\n        if \"from langflow\" in text:\n            msg = (\n                f\"{src} imports from ``langflow`` -- the bundle is installed \"\n                \"against the public BUNDLE_API surface (lfx), not Langflow \"\n                \"internals.  Either rewrite the import or leave the component \"\n                \"in-tree.  See src/bundles/PORTING.md § 0.\"\n            )\n            raise SystemExit(msg)\n        classes = () if src.name == \"__init__.py\" else _discover_classes(text)\n        component_files.append(ComponentFile(path=src, classes=classes))\n\n    if not component_files and not nested_subdirs:\n        msg = f\"No ``*.py`` files under {in_tree}; nothing to port.\"","sourceCodeStart":698,"sourceCodeEnd":734,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/scripts/migrate/port_bundle.py#L698-L734","documentation":"During eligibility scanning, port_bundle.py walks every *.py file — including files inside nested subpackages (rglob, skipping __pycache__) — and rejects any file containing the literal text 'from langflow'. Out-of-tree bundles must depend only on the public lfx BUNDLE_API surface, not Langflow-internal imports, because the published lfx-<bundle> distribution cannot depend on the langflow package. Hitting this text in a nested file aborts the port with the file path and a pointer to PORTING.md § 0.","triggerScenarios":"Running port_bundle.py --bundle <name> where any module inside a nested subpackage (e.g. <provider>/helpers/utils.py) has `from langflow.xxx import ...`. The check is a literal substring match, so even a commented-out or docstring occurrence triggers it.","commonSituations":"Older components written against the langflow package before the lfx split; helper utilities importing langflow.logging or schema internals; stale comments mentioning 'from langflow' after the code was already migrated.","solutions":["Open the named file and rewrite `from langflow.X` to the lfx equivalent (usually `from lfx.X`) using the public BUNDLE_API surface.","If the import has no lfx equivalent, the component cannot be ported — leave it in-tree and port only the rest, or refactor the dependency out.","Remove literal 'from langflow' occurrences from comments/docstrings (the check is textual)."],"exampleFix":"# before (helpers/utils.py)\nfrom langflow.logging import logger\n\n# after (helpers/utils.py)\nfrom lfx.logging import logger","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef clean_of_langflow_imports(provider_dir: Path) -> list[Path]:\n    \"\"\"Mirror of the port guard: list offending files (nested included).\"\"\"\n    return [\n        p for p in provider_dir.rglob(\"*.py\")\n        if \"__pycache__\" not in p.parts and \"from langflow\" in p.read_text(encoding=\"utf-8\")\n    ]\n\nassert not clean_of_langflow_imports(LFX_COMPONENTS / bundle)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run a repo-wide grep for 'from langflow' inside the provider dir before invoking port_bundle.py — the guard is a literal substring match, comments included.","Bundles may only use the lfx BUNDLE_API; if an import has no lfx equivalent the component stays in-tree.","Rewrite imports to lfx.* equivalents in a separate commit before the port commit so diffs stay reviewable."],"tags":["migration","imports","packaging","validation"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}