fix: rebuild drifted built-in components instead of refusing the flow (#14527)
* fix: rebuild drifted built-ins instead of refusing the flow With LANGFLOW_ALLOW_CUSTOM_COMPONENTS=false the code stored in a node never runs: resolve_trusted_code_for_build substitutes this server's copy, keyed by code hash, before exec. check_flow_and_raise nevertheless refused the whole flow when a node's stored code hash did not match the current template, so any upgrade that touched a built-in blocked every saved flow using it until each node was updated by hand. The unauthenticated public build path already avoids this: prepare_public_flow_build swaps in the server's code by component type and refuses only unrecognized types. Apply the same swap-by-type rule on the normal build path, behind LANGFLOW_SUBSTITUTE_OUTDATED_COMPONENT_CODE (default true, no effect while custom components are allowed). Graph.from_payload rewrites recognized built-ins whose stored code has drifted, right after the extension migration pass and before validation, so check_flow_and_raise passes on its own and unrecognized types still reach it and are still blocked. Pre-check call sites that do not own the payload agree about which types are substitutable rather than refusing a build that would then succeed. Nothing new becomes runnable: the code that executes is always this server's own, selected by component type. Every swap is logged, and the stored flow is left untouched so the editor keeps flagging the node as outdated. Fixes #14455 * fix(lfx): never substitute a component type two components claim A type alias can be contributed by two different components -- a built-in and a components_path component of the same name, or an XComponent/X pair across bundles. collect_component_hash_lookups keeps every such hash, but collect_component_code_lookups is first-wins by registry iteration order, so its single code value may belong to either one. Stored code matching neither hash is then indistinguishable from "this node is the other component", and the substitution would rebuild the node as the wrong component. Gate the swap on the type resolving to exactly one current hash, expressed once as SubstitutableComponentTypes and shared by both sides: the substitution pass in from_payload and the pre-check in validate_flow_for_current_settings. Sharing the rule is what keeps them in agreement -- exempting an ambiguous type from the outdated check without substituting it would let the node clear validation carrying code resolve_trusted_code_for_build then fails on, with a far less actionable message. Ambiguous types are now neither substituted nor exempted, so they stay blocked as outdated exactly as before this pass existed. Also assert the endpoint test's substitution actually fires, via a spy on the real run, instead of only asserting the 200 it would return anyway once the fixture's stored code caught up with the server's. Both found by CodeRabbit on #14527. * fix: align trusted component substitution policies * fix: validate final public component source * test: isolate live provider checks * test: classify remaining live service checks * test: keep comet client construction local
E
Eric Hare committed
5c80b497886951b9fe9bba1b3d7d45a15b64130b
Parent: 7b35e88
Committed by GitHub <noreply@github.com>
on 8/13/2026, 8:13:27 PM