#15067·langflow

Custom component reload silently resolves to the wrong class when a file defines more than one Component subclass

Author: mjobrien05Created Sep 14, 2026Updated Sep 14, 2026

Bug Description

Langflow captures a custom component's entire containing source file as its stored code field, and rebuilds a node from that stored code by taking "the first class in the file that subclasses Component" (extract_class_name, src/lfx/src/lfx/custom/validate.py):

def extract_class_name(code: str) -> str:
    """Extract the name of the first Component subclass found in the code."""
    module = ast.parse(code)
    for node in module.body:
        if not isinstance(node, ast.ClassDef):
            continue
        for base in node.bases:
            if isinstance(base, ast.Name) and any(pattern in base.id for pattern in ["Component", "LC"]):
                return node.name
    ...

If a custom-components directory file defines more than one Component subclass (a natural way to organize a related bundle of components — several small components sharing helpers, in one file), every node built from that file's stored code — regardless of which class it's actually supposed to be — silently resolves to the same class: whichever one is textually first. There's no validation anywhere in the pipeline that catches this; it only surfaces once a node is rebuilt from its stored template (e.g. loading a saved flow), not when the class is freshly imported directly.

The failure mode is actively misleading. Building a node meant to be IngestPrep while the file's first class is ClaimClip produces:

Error building Component Ingest Prep: Attribute video_data_out not found in ClaimClip

— an attribute error that gives no indication the real cause is "wrong class was instantiated," making this very hard to diagnose without already suspecting the loader mechanism.

Reproduction

  1. In a custom_components directory, create one file with two Component subclasses, e.g.:
from lfx.custom.custom_component.component import Component
from lfx.io import Output
from lfx.schema.data import Data

class First(Component):
    display_name = "First"
    name = "First"
    outputs = [Output(display_name="Out", name="out", method="run")]
    def run(self) -> Data:
        return Data(data={"which": "First"})

class Second(Component):
    display_name = "Second"
    name = "Second"
    outputs = [Output(display_name="Out2", name="out2", method="run2")]
    def run2(self) -> Data:
        return Data(data={"which": "Second"})
  1. Add a Second node to a flow, save it, and reopen/rebuild the flow (or otherwise trigger a rebuild from the stored template rather than a fresh import — e.g. reloading the saved flow JSON via Graph.from_payload).
  2. The rebuilt node instantiates First, not Second — calling its run2/out2 output raises an attribute error referencing First, not the class the node was configured as.

Expected behavior

Either:

  • Reject a custom-components file containing more than one Component subclass at load/validation time with a clear error naming the file and both classes, or
  • Select the class by the node's own stored type/class-name metadata rather than "first class found in the source," so multiple components can safely share one file.

Who can help?

No response — filed from direct debugging, not familiar with current code ownership for lfx/custom.