Skip to content

Fix/prevent duplicate flow creation#11016

Closed
AntonioABLima wants to merge 0 commit into
langflow-ai:mainfrom
AntonioABLima:fix/prevent-duplicate-flow-creation
Closed

Fix/prevent duplicate flow creation#11016
AntonioABLima wants to merge 0 commit into
langflow-ai:mainfrom
AntonioABLima:fix/prevent-duplicate-flow-creation

Conversation

@AntonioABLima
Copy link
Copy Markdown
Member

@AntonioABLima AntonioABLima commented Dec 15, 2025

Description

This PR addresses an issue where users in high-latency environments could unintentionally create duplicate flows.

Problem:
Previously, when a user clicked on a template card, the request to create the flow was initiated, but the UI remained interactive. If the internet connection was slow, the user could click the card multiple times while the first request was still pending, resulting in multiple identical flows being created.

Solution:
I updated the TemplateGetStartedCardComponent to include a local loading state.

  • The component now tracks whether a creation request is in progress.
  • Subsequent clicks are blocked if loading is true.
  • Added visual feedback: the card opacity is reduced, the cursor changes to wait, and the arrow icon is replaced by a spinner to indicate activity.
  • Used .finally() to ensure the loading state is reset properly even if the request fails.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Manual Test: I simulated a slow network connection (Slow 3G) using browser DevTools.
  • Scenario: Clicked the template card multiple times in rapid succession.
  • Result: Confirmed that only one flow creation request was sent and the UI correctly prevented further interactions until the process was complete (or navigation occurred).

Summary by CodeRabbit

  • New Features

    • Added Streamable HTTP transport support for MCP server connectivity
    • Introduced API key source configuration options for enhanced security flexibility
  • Bug Fixes

    • Improved error handling for missing keys in API response sanitization
    • Enhanced profile picture access validation and path safety
  • Security

    • Added authentication requirements to multiple API endpoints
    • Centralized authorization checks for file and resource access
    • Restricted sensitive configuration exposure
  • Chores

    • Updated core dependencies for compatibility and performance
    • Refined workflow build processes and release automation

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 15, 2025

Walkthrough

This PR introduces comprehensive authentication hardening across multiple API endpoints, adds Streamable HTTP transport support for MCP alongside SSE, refactors file access patterns with centralized authorization and path safety, updates CI/CD workflows for dependency and wheel handling, and modernizes starter project templates with enhanced component logic, improved session handling, and expanded provider support.

Changes

Cohort / File(s) Change Summary
Environment & Dependencies
.env.example, pyproject.toml
Added LANGFLOW_API_KEY_SOURCE and LANGFLOW_API_KEY configuration options for API key validation; upgraded cuga to ~=0.1.11 and agent-lifecycle-toolkit to ~=0.4.4
CI/CD Workflows
.github/workflows/cross-platform-test.yml, .github/workflows/nightly_build.yml, .github/workflows/release.yml, .github/workflows/release_nightly.yml
Added --prerelease=allow flag to uv pip install commands; removed uv lock step from nightly build; replaced dynamic version extraction with deterministic release_tag version; changed wheel packaging to --no-sources --wheel
Security Baseline
.secrets.baseline
Added new secret entries for starter projects and adjusted line_number reference in auth utils; updated generation timestamp
Startup & Debug
src/backend/base/langflow/__main__.py
Removed debug logging line in set_var_for_macos_issue
API Security — Authentication
src/backend/base/langflow/api/log_router.py, src/backend/base/langflow/api/v1/chat.py, src/backend/base/langflow/api/v1/endpoints.py, src/backend/base/langflow/api/v1/monitor.py, src/backend/base/langflow/api/v1/validate.py, src/backend/base/langflow/api/v1/users.py, src/backend/base/langflow/api/v2/registration.py
Added Depends(get_current_active_user) or Depends(get_current_active_superuser) authentication requirements to log endpoints, chat endpoints, config endpoints, monitor endpoints, validation endpoints, user creation, and registration endpoints
API Security — Core Utilities
src/backend/base/langflow/api/utils/core.py
Added guard to prevent KeyError when "name" key is missing in remove_api_keys
File Management — API v1
src/backend/base/langflow/api/v1/files.py
Consolidated get_flow authorization (now returns 404 on mismatch); added _get_allowed_profile_picture_folders helper with dynamic folder discovery; enhanced path traversal validation and fallback to package assets for profile pictures; refactored download_file and download_image to use Depends(get_flow)
File Management — API v2
src/backend/base/langflow/api/v2/files.py
Changed error handling: fetch_file_object returns 404 instead of 403 on access mismatch; upload_user_file returns 500 instead of 403 on PermissionError; refactored download_file streaming to pre-check file existence
MCP Transport — Utilities
src/backend/base/langflow/api/utils/mcp/__init__.py, src/backend/base/langflow/api/utils/mcp/config_utils.py
Imported and exported new URL functions (get_composer_streamable_http_url, get_project_streamable_http_url); added _get_project_base_url_components and _get_mcp_composer_auth_config helpers; replaced SSE URLs with streamable HTTP URLs in MCP server creation; added --transport streamablehttp argument
MCP Transport — Routes
src/backend/base/langflow/api/v1/mcp.py, src/backend/base/langflow/api/v1/mcp_projects.py
Added ResponseNoOp class to prevent duplicate ASGI responses; introduced StreamableHTTPSessionManager integration with lifecycle management; added new StreamableHTTP class and manager functions (start/stop/get); added /mcp/streamable endpoints; added per-project streamable HTTP support with ProjectMCPServer and ProjectTaskGroup; replaced SSE-only flows with dual transport support; updated composer URL responses to include both streamable_http_url and legacy_sse_url
MCP Utilities & Config
src/backend/base/langflow/api/v1/mcp_utils.py, src/backend/base/langflow/api/v1/schemas.py
Added MCP_SERVERS_FILE constant; added robust current user handling in resource listing; updated file download URL construction; added user-owned file listing; introduced ComposerUrlResponse schema with streamable_http_url and legacy_sse_url fields; added optional transport field to MCPInstallRequest
Project Management
src/backend/base/langflow/api/v1/projects.py
Replaced SSE URL with get_project_streamable_http_url for MCP proxy; added --transport streamablehttp to all MCP proxy invocations
Flow Helpers
src/backend/base/langflow/helpers/flow.py
Added SORT_DISPATCHER constant for asc/desc sorting; added list_flows_by_flow_folder, list_flows_by_folder_id, and get_flow_by_id_or_name helpers with sorting and filtering support
Starter Projects — Chat Components
src/backend/base/langflow/initial_setup/starter_projects/*.json (Basic Prompt Chaining, Basic Prompting, Blog Writer, Custom Component Generator, Document Q&A, Financial Report Parser, Image Sentiment Analysis, Instagram Copywriter, Invoice Summarizer, Knowledge Ingestion, Knowledge Retrieval, Meeting Summary, Memory Chatbot, Price Deal Finder, Research Agent)
Updated ChatInput/ChatOutput documentation URLs; enhanced session_id handling with fallback to graph.session_id; expanded message_response logic with source building, serialization, and validation helpers; updated code_hash values and FastAPI dependencies (0.120.0 → 0.123.0); added support for existing message reuse
Starter Projects — Agent & File Components
src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json, others
Expanded File component with Docling-based processing support (subprocess isolation, S3/local storage, temp file management); added FileComponent processing methods (_validate_and_resolve_paths, _is_docling_compatible, etc.); added WatsonxAI and Ollama provider support to Agent components; extended agent inputs with max_output_tokens, base_url, project_id, max_retries, model_kwargs; added override_skip, track_in_telemetry, and external_options metadata across inputs

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant MCP Endpoint
    participant StreamableHTTP Manager
    participant MCP Server
    participant Context

    Client->>MCP Endpoint: Request to /mcp/streamable
    MCP Endpoint->>Context: Get current user context
    MCP Endpoint->>StreamableHTTP Manager: get_streamable_http_manager()
    StreamableHTTP Manager-->>MCP Endpoint: Return active session manager
    MCP Endpoint->>MCP Server: Delegate request handling
    MCP Server-->>MCP Endpoint: Process request
    MCP Endpoint->>Context: Reset user context token
    MCP Endpoint->>Client: ResponseNoOp (no duplicate response)

    Note over MCP Endpoint,MCP Server: New Streamable HTTP transport<br/>alongside existing SSE transport
Loading
sequenceDiagram
    participant User
    participant API Endpoint
    participant Dependency Injection
    participant Auth Service
    participant Handler

    User->>API Endpoint: Request to protected endpoint
    API Endpoint->>Dependency Injection: Resolve Depends(get_current_active_user)
    Dependency Injection->>Auth Service: get_current_active_user()
    Alt User authenticated
        Auth Service-->>Dependency Injection: Return User object
        Dependency Injection-->>API Endpoint: Inject user parameter
        API Endpoint->>Handler: Process request with current_user
        Handler-->>User: Return response
    Else User not authenticated
        Auth Service-->>API Endpoint: Raise authentication error
        API Endpoint-->>User: 401 Unauthorized
    End

    Note over API Endpoint,Auth Service: Authentication hardening<br/>across multiple endpoints
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas requiring extra attention:

  • MCP Transport Refactoring (src/backend/base/langflow/api/v1/mcp.py, src/backend/base/langflow/api/v1/mcp_projects.py) — New StreamableHTTP class, lifecycle management with start/stop, context propagation in request handling, and dual transport coordination need careful verification for correctness and thread safety.
  • File Access Security (src/backend/base/langflow/api/v1/files.py) — Path traversal prevention logic in download_profile_picture, allowed folder validation, and fallback to package assets require thorough security review to ensure containment checks are complete.
  • API Authentication Changes (src/backend/base/langflow/api/v1/chat.py, src/backend/base/langflow/api/v1/endpoints.py, src/backend/base/langflow/api/v1/monitor.py) — Verify that new Depends() injection is correctly applied to all endpoints, especially those with custom decorators or multiple route variations.
  • Starter Project JSON Updates — Verify consistency of ChatInput/ChatOutput session_id handling logic across 15+ starter projects and completeness of code_hash updates; check that all new Agent fields (override_skip, track_in_telemetry, external_options) are correctly defined across all providers (OpenAI, Anthropic, Google, Ollama, WatsonxAI).
  • ProjectTaskGroup Lifecycle (src/backend/base/langflow/api/v1/mcp_projects.py) — Ensure proper startup/shutdown sequencing and verify that concurrent run() violations are prevented.

Possibly related PRs

Suggested labels

authentication, mcp, refactor, security

Suggested reviewers

  • lucaseduoli
  • mfortman11

Pre-merge checks and finishing touches

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 3 warnings)
Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error PR introduces 27+ files with major API, authentication, MCP, and helper function changes but contains no automated test coverage (no test_*.py files, conftest.py, or pytest configuration found). Add regression tests for duplicate flow fix, unit tests for MCP streamable HTTP endpoints, authentication requirement changes, file authorization logic, and helper functions, plus integration tests for MCP Composer URL generation.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Test Quality And Coverage ⚠️ Warning PR introduces significant new API endpoints and backend functionality without any accompanying test files or test coverage. Add comprehensive pytest tests for new async functions, API endpoints, and security enhancements with both success and error path coverage.
Test File Naming And Structure ⚠️ Warning PR introduces 23+ backend files and frontend component changes but includes no test files (test_*.py, *.test.ts, *.test.tsx) validating these modifications. Add unit tests for flow helpers, authentication changes, MCP utilities; add frontend tests for duplicate prevention loading state; add security tests for file authorization.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix/prevent duplicate flow creation' directly matches the main objective: preventing duplicate flows from being created through multiple clicks. It clearly summarizes the primary bug fix.
Excessive Mock Usage Warning ✅ Passed No test files are included in this PR's modifications, making the excessive mock usage check not applicable.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json (2)

472-488: Fix isinstance calls using union types in ChatOutput._validate_input.

_validate_input currently uses isinstance(item, Message | Data | DataFrame | str) and isinstance(self.input_value, Message | Data | DataFrame | str | list | Generator | type(None)). The second argument to isinstance must be a type or tuple of types; passing a PEP 604 union can raise TypeError at runtime.

You can keep the same semantics with tuples:

-        if isinstance(self.input_value, list) and not all(
-            isinstance(item, Message | Data | DataFrame | str) for item in self.input_value
-        ):
+        if isinstance(self.input_value, list) and not all(
+            isinstance(item, (Message, Data, DataFrame, str)) for item in self.input_value
+        ):
@@
-        if not isinstance(
-            self.input_value,
-            Message | Data | DataFrame | str | list | Generator | type(None),
-        ):
+        if not isinstance(
+            self.input_value,
+            (Message, Data, DataFrame, str, list, Generator, type(None)),
+        ):

1190-1287: Align Docling advanced processing with per-file rollup in FileComponent.process_files.

In the advanced Docling branch, process_files loops over each file but calls self.rollup_data(file_list, rows) (and similarly with [advanced_data]). This mixes a single file’s rows with the full file_list, which is likely to mis-associate rows and files when more than one file is processed.

To keep the mapping correct, use the current file instead of the whole file_list:

-            final_return: list[BaseFileComponent.BaseFile] = []
+            final_return: list[BaseFileComponent.BaseFile] = []
             for file in file_list:
                 file_path = str(file.path)
                 advanced_data: Data | None = self._process_docling_in_subprocess(file_path)
@@
-                    final_return.extend(self.rollup_data(file_list, rows))
+                    final_return.extend(self.rollup_data([file], rows))
@@
-                    final_return.extend(self.rollup_data([file], [empty_data]))
+                    final_return.extend(self.rollup_data([file], [empty_data]))
                 else:
                     # If not structured, keep as-is (e.g., markdown export or error dict)
-                    final_return.extend(self.rollup_data(file_list, [advanced_data]))
+                    final_return.extend(self.rollup_data([file], [advanced_data]))

This keeps each Docling result correctly bound to its originating file while preserving the unnesting behavior.

src/backend/base/langflow/api/v1/mcp_projects.py (1)

688-688: generated_api_key is always None.

The variable generated_api_key is initialized to None at line 688 but never assigned a value. The conditional at line 860 will always be False, making the associated message unreachable.

Either assign generated_api_key when an API key is created (around line 776), or remove the dead code:

             if should_generate_api_key:
                 async with session_scope() as api_key_session:
                     api_key_create = ApiKeyCreate(name=f"MCP Server {project.name}")
                     api_key_response = await create_api_key(api_key_session, api_key_create, current_user.id)
                     langflow_api_key = api_key_response.api_key
+                    generated_api_key = langflow_api_key
                     args.extend(["--headers", "x-api-key", langflow_api_key])

Also applies to: 860-862

♻️ Duplicate comments (2)
src/backend/base/langflow/initial_setup/starter_projects/Knowledge Retrieval.json (1)

235-311: ChatOutput here matches the validated implementation; no additional issues.

This ChatOutput instance embeds the same implementation and FastAPI/orjson dependency versions as in the Meeting Summary starter. The earlier review of that code/deps applies here as well; behavior and types are consistent.

As with the other starters, ensure the pinned fastapi and orjson versions are present and compatible in your runtime.

src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json (1)

479-496: Apply the same ChatOutput._validate_input fix here as in “Document Q&A”.

This file embeds the same ChatOutput implementation, including the isinstance(..., Message | Data | DataFrame | str) and isinstance(..., Message | Data | DataFrame | str | list | Generator | type(None)) patterns. Please update them to use tuples of types as shown in the earlier comment on Document Q&A.json so both starter projects share the corrected implementation.

🧹 Nitpick comments (21)
src/backend/base/langflow/api/v1/users.py (1)

25-48: LGTM! Security hardening correctly implemented.

The addition of superuser authentication to the user creation endpoint is an important security improvement that prevents unauthorized account creation. The implementation correctly uses Depends(get_current_active_superuser) and the docstring accurately documents the requirement.

Optional: Consider using the dependencies pattern for consistency.

Since current_user is not used in the function body (hence the # noqa: ARG001), consider using the dependencies pattern in the decorator for consistency with the read_all_users endpoint on line 59:

-@router.post("/", response_model=UserRead, status_code=201)
+@router.post("/", response_model=UserRead, status_code=201, dependencies=[Depends(get_current_active_superuser)])
 async def add_user(
     user: UserCreate,
     session: DbSession,
-    current_user: Annotated[User, Depends(get_current_active_superuser)],  # noqa: ARG001
 ) -> User:

This makes it more explicit that the dependency is purely for authentication and avoids the need for the noqa comment. Both approaches are functionally equivalent, but the dependencies pattern is generally preferred when the injected value isn't used.

src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json (1)

1241-1299: Add missing display_name to agent_llm dropdown.

The agent_llm dropdown input (lines 1241-1299) is missing a display_name field in the nested component definition. While it has a default label "Model Provider", ensure the field structure is complete and consistent with other input fields in the template.

src/backend/base/langflow/api/v1/mcp_utils.py (2)

96-101: Overly broad exception catch may hide bugs.

Catching Exception with # noqa: BLE001 suppresses linting but may hide unexpected errors beyond typical LookupError from ContextVar.get(). Consider catching LookupError specifically, which is what ContextVar.get() raises when no value is set.

-        try:
-            current_user = current_user_ctx.get()
-        except Exception as e:  # noqa: BLE001
-            msg = f"Error getting current user: {e!s}"
-            await logger.aexception(msg)
-            current_user = None
+        try:
+            current_user = current_user_ctx.get()
+        except LookupError:
+            await logger.adebug("No current user context set, skipping user files listing")
+            current_user = None

139-140: Potential issue: stored_filename may be empty if both path and name are empty.

If stored_path is empty and user_file.name is also empty/None, stored_filename could be empty, leading to invalid resource URIs.

+                    if not stored_filename:
+                        await logger.adebug(f"Skipping user file with empty filename: {user_file.id}")
+                        continue
                     safe_filename = quote(stored_filename)
src/backend/base/langflow/helpers/flow.py (4)

52-57: Mutable default argument is a known anti-pattern.

While # noqa: B006 suppresses the linter, using a mutable default (dict) can lead to unexpected behavior if the dict is ever mutated. Consider using None as default with initialization inside the function, similar to list_flows_by_folder_id.

 async def list_flows_by_flow_folder(
     *,
     user_id: str | None = None,
     flow_id: str | None = None,
-    order_params: dict | None = {"column": "updated_at", "direction": "desc"},  # noqa: B006
+    order_params: dict | None = None,
 ) -> list[Data]:
+    if order_params is None:
+        order_params = {"column": "updated_at", "direction": "desc"}

86-86: Accessing private _mapping attribute is fragile.

Using flow._mapping (noqa: SLF001) relies on SQLAlchemy's internal Row representation. This could break with SQLAlchemy version updates. Consider using a more explicit conversion.

-            return [Data(data=dict(flow._mapping)) for flow in flows]  # noqa: SLF001
+            return [Data(data={"id": flow.id, "name": flow.name, "updated_at": flow.updated_at}) for flow in flows]

141-150: Redundant validation after initial check.

Lines 148-150 check if not (attr and val) but this condition is impossible to reach given the preceding logic. If flow_name is truthy, attr and val are set. If flow_id is truthy, they're overwritten. The initial check at line 135 ensures at least one is truthy.

-    if not (attr and val):
-        msg = "Flow id or Name is required"
-        raise ValueError(msg)

114-117: Redundant order_params is not None check.

At line 102-103, order_params is guaranteed to be non-None after the initialization. The check at line 114 is always true.

-            if order_params is not None:
-                sort_col = getattr(Flow, order_params.get("column", "updated_at"), Flow.updated_at)
-                sort_dir = SORT_DISPATCHER.get(order_params.get("direction", "desc"), desc)
-                stmt = stmt.order_by(sort_dir(sort_col))
+            sort_col = getattr(Flow, order_params.get("column", "updated_at"), Flow.updated_at)
+            sort_dir = SORT_DISPATCHER.get(order_params.get("direction", "desc"), desc)
+            stmt = stmt.order_by(sort_dir(sort_col))
src/backend/base/langflow/api/v1/schemas.py (1)

473-475: Consider documenting the transport field options.

Adding a Field description would improve API documentation clarity for consumers.

 class MCPInstallRequest(BaseModel):
     client: str
-    transport: Literal["sse", "streamablehttp"] | None = None
+    transport: Literal["sse", "streamablehttp"] | None = Field(
+        default=None,
+        description="Transport protocol: 'sse' for Server-Sent Events or 'streamablehttp' for Streamable HTTP"
+    )
src/backend/base/langflow/api/v2/files.py (1)

229-230: Generic error message hides debugging information.

While returning 500 for server config issues is correct, the generic "Error accessing storage" message may make troubleshooting difficult. Consider logging the actual error while returning the generic message.

         except PermissionError as e:
             # Access denied or invalid credentials - return 500 as this is a server config issue
+            await logger.aerror(f"Storage permission error for user {current_user.id}: {e}")
             raise HTTPException(status_code=500, detail="Error accessing storage") from e
src/backend/base/langflow/initial_setup/starter_projects/Meeting Summary.json (1)

1656-1720: MemoryComponent logic looks consistent with existing patterns.

MemoryComponent’s store/retrieve paths, telemetry, and type usage (Data/DataFrame/Message) are coherent and match typical Langflow memory patterns; no correctness or safety issues stand out here in this starter snapshot.

You may eventually want to tighten the return typing of retrieve_messages (it returns a list but is cast to Data) for better static checking, but this is non‑blocking for the starter.

src/backend/base/langflow/api/v1/chat.py (3)

58-131: retrieve_vertices_order endpoint logic is reasonable; consider minor robustness tweaks.

The new /build/{flow_id}/vertices endpoint correctly:

  • Reuses build_graph_from_db / build_and_cache_graph_from_data,
  • Sets a fresh run_id and caches the graph for later use, and
  • Logs playground telemetry in both success and error paths.

Two small refinements you might consider (non‑blocking):

  • Use if data is None: instead of if not data: to avoid depending on the truthiness of FlowDataRequest.
  • vertices_to_run is built via set.union(...), which makes ordering implementation‑defined; if the client relies on a stable order, returning a sorted list (or preserving the original sequence) would be safer.

201-216: Auth‑gating GET /build/{job_id}/events may block public flows; verify call paths.

Requiring get_current_active_user for the events endpoint is appropriate for authenticated builds, but note that:

  • build_public_tmp explicitly supports unauthenticated “public” flows and still returns job_id when event_delivery != EventDeliveryType.DIRECT.
  • There is no separate unauthenticated events route for public jobs; clients would need to call this same /build/{job_id}/events, which now requires auth.

If any callers rely on polling/streaming events for public flows without authentication, those flows will now fail with 401. Either:

  • Ensure public flows always use event_delivery=EventDeliveryType.DIRECT so they never hit this route, or
  • Introduce a dedicated events endpoint (or conditional auth) for public jobs.

219-255: cancel_build now returns a structured response and has clearer error handling.

The updated cancel_build endpoint:

  • Enforces authentication,
  • Returns a typed CancelFlowResponse with a clear success flag and message, and
  • Distinguishes between normal failures, job‑not‑found (ValueError / JobQueueNotFoundError → 404), and unexpected errors (500).

Behaviorally this is much easier for clients to reason about. One thing to keep in mind is that generic exceptions are surfaced via str(exc) in the HTTP 500, which may leak internal details; if that’s a concern, you might want to replace it with a generic message and keep details in logs only.

src/backend/base/langflow/api/v1/files.py (3)

179-207: Comprehensive path traversal protection.

The multi-layered defense is well implemented:

  1. Reject .. patterns explicitly
  2. Validate against allowed folder whitelist
  3. Reject path separators in filename
  4. Use resolve() + startswith() for symlink-aware containment check

However, the startswith() check on string paths can have edge cases (e.g., /allowed_base vs /allowed_base_other).

Consider using Path.is_relative_to() (Python 3.9+) for more robust containment:

-        if not str(file_path).startswith(str(allowed_base)):
+        try:
+            file_path.relative_to(allowed_base)
+        except ValueError:
             # Return 404 to prevent path traversal attempts from revealing system structure
             raise HTTPException(status_code=404, detail="Profile picture not found")

216-220: Apply consistent path containment check for package fallback.

The same startswith() consideration applies to the package path validation. For consistency, apply the same improvement here.

-            if not str(package_path).startswith(str(allowed_package_base)):
+            try:
+                package_path.relative_to(allowed_package_base)
+            except ValueError:
                 # Return 404 to prevent path traversal attempts from revealing system structure
                 raise HTTPException(status_code=404, detail="Profile picture not found")

49-53: Consider more specific exception handling.

The bare except Exception catches all exceptions including KeyboardInterrupt and SystemExit (though those derive from BaseException). The logging is good, but consider catching more specific exceptions.

-    except Exception as _:
-        import logging
-
-        logger = logging.getLogger(__name__)
-        logger.exception("Exception occurred while getting allowed profile picture folders")
+    except OSError:
+        # File system errors when enumerating directories
+        import logging
+        logger = logging.getLogger(__name__)
+        logger.exception("Exception occurred while getting allowed profile picture folders")
src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json (1)

2780-2780: Review extensive Agent component code changes for correctness.

The Agent component's embedded code (line 2780 onwards) underwent a major refactoring introducing:

  • Dynamic provider switching with MODEL_PROVIDERS_DICT
  • Structured output support with schema validation
  • Multiple new async methods (get_agent_requirements, get_llm, json_response, get_memory_data)
  • Complex field visibility and configuration logic in update_build_config

Risks to assess:

  • The code is deeply embedded in a JSON configuration file, making maintenance and testing difficult
  • Multiple levels of indirection in provider handling (MODEL_PROVIDERS_DICT → component_class → _build_llm_model)
  • Exception handling catches broad Exception types that could mask unexpected errors
  • Memory data retrieval logic filters messages by ID comparison but doesn't validate message object structure

Consider refactoring this embedded code into separate, testable Python modules.

src/backend/base/langflow/api/v1/mcp_projects.py (3)

427-433: Use %-style formatting for logger calls.

Using f-strings in logger calls evaluates the string even when the log level is disabled. Prefer %-style formatting for better performance.

     try:
         await project_server.session_manager.handle_request(request.scope, request.receive, request._send)  # noqa: SLF001
     except HTTPException:
         raise
     except Exception as exc:
-        await logger.aexception(f"Error handling Streamable HTTP request for project {project_id}: {exc!s}")
+        await logger.aexception("Error handling Streamable HTTP request for project %s: %s", project_id, exc)
         raise HTTPException(status_code=500, detail="Internal server error in project MCP transport") from exc

1251-1265: Use %-style formatting for logger calls in _run_session_manager.

Multiple f-strings are used in logger calls within this method. Prefer %-style formatting for consistency and performance.

     async def _run_session_manager(self, *, task_status: TaskStatus[None] = anyio.TASK_STATUS_IGNORED):
         """Own the lifecycle of the project's Streamable HTTP session manager."""
         try:
             async with self.session_manager.run():
                 self._manager_started = True  # set flag before unblocking task (ensures waiting requests proceed)
                 task_status.started()  # unblock
                 await anyio.sleep_forever()
         except anyio.get_cancelled_exc_class():
-            await logger.adebug(f"Streamable HTTP manager cancelled for project {self.project_id}")
+            await logger.adebug("Streamable HTTP manager cancelled for project %s", self.project_id)
         except Exception as e:
-            await logger.aexception(f"Error in session manager for project {self.project_id}: {e}")
+            await logger.aexception("Error in session manager for project %s: %s", self.project_id, e)
             raise
         finally:
             self._manager_started = False
-            await logger.adebug(f"Streamable HTTP manager stopped for project {self.project_id}")
+            await logger.adebug("Streamable HTTP manager stopped for project %s", self.project_id)

1319-1326: Use %-style formatting for logger call.

             try:  # https://anyio.readthedocs.io/en/stable/cancellation.html, https://docs.python.org/3/library/asyncio-task.html#asyncio.Task.cancel
                 self._task_group.cancel_scope.cancel()  # type: ignore[union-attr]
                 await self._tg_task  # type: ignore[misc]
             except Exception as e:  # noqa: BLE001
-                await logger.aexception(f"Failed to stop project task group: {e}")
+                await logger.aexception("Failed to stop project task group: %s", e)
             finally:
                 self._cleanup()
                 await logger.adebug("Project MCP task group stopped")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 909bdb1 and c10d986.

⛔ Files ignored due to path filters (2)
  • src/frontend/package-lock.json is excluded by !**/package-lock.json
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (41)
  • .env.example (1 hunks)
  • .github/workflows/cross-platform-test.yml (5 hunks)
  • .github/workflows/nightly_build.yml (0 hunks)
  • .github/workflows/release.yml (1 hunks)
  • .github/workflows/release_nightly.yml (1 hunks)
  • .secrets.baseline (3 hunks)
  • pyproject.toml (1 hunks)
  • src/backend/base/langflow/__main__.py (0 hunks)
  • src/backend/base/langflow/api/log_router.py (3 hunks)
  • src/backend/base/langflow/api/utils/core.py (1 hunks)
  • src/backend/base/langflow/api/utils/mcp/__init__.py (1 hunks)
  • src/backend/base/langflow/api/utils/mcp/config_utils.py (6 hunks)
  • src/backend/base/langflow/api/v1/chat.py (4 hunks)
  • src/backend/base/langflow/api/v1/endpoints.py (2 hunks)
  • src/backend/base/langflow/api/v1/files.py (7 hunks)
  • src/backend/base/langflow/api/v1/mcp.py (4 hunks)
  • src/backend/base/langflow/api/v1/mcp_projects.py (28 hunks)
  • src/backend/base/langflow/api/v1/mcp_utils.py (6 hunks)
  • src/backend/base/langflow/api/v1/monitor.py (6 hunks)
  • src/backend/base/langflow/api/v1/projects.py (4 hunks)
  • src/backend/base/langflow/api/v1/schemas.py (1 hunks)
  • src/backend/base/langflow/api/v1/users.py (1 hunks)
  • src/backend/base/langflow/api/v1/validate.py (2 hunks)
  • src/backend/base/langflow/api/v2/files.py (3 hunks)
  • src/backend/base/langflow/api/v2/registration.py (2 hunks)
  • src/backend/base/langflow/helpers/flow.py (3 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Basic Prompt Chaining.json (5 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting.json (5 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json (9 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Custom Component Generator.json (7 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json (9 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Financial Report Parser.json (10 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Image Sentiment Analysis.json (8 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json (25 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json (23 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Knowledge Ingestion.json (5 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Knowledge Retrieval.json (6 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Meeting Summary.json (13 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json (7 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json (23 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json (23 hunks)
💤 Files with no reviewable changes (2)
  • .github/workflows/nightly_build.yml
  • src/backend/base/langflow/main.py
🧰 Additional context used
📓 Path-based instructions (3)
src/backend/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)

src/backend/**/*.py: Use FastAPI async patterns with await for async operations in component execution methods
Use asyncio.create_task() for background tasks and implement proper cleanup with try/except for asyncio.CancelledError
Use queue.put_nowait() for non-blocking queue operations and asyncio.wait_for() with timeouts for controlled get operations

Files:

  • src/backend/base/langflow/api/log_router.py
  • src/backend/base/langflow/api/v1/schemas.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/api/v1/validate.py
  • src/backend/base/langflow/api/v1/users.py
  • src/backend/base/langflow/api/v1/files.py
  • src/backend/base/langflow/api/v1/monitor.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/helpers/flow.py
  • src/backend/base/langflow/api/v2/registration.py
  • src/backend/base/langflow/api/v1/projects.py
  • src/backend/base/langflow/api/v1/mcp.py
  • src/backend/base/langflow/api/v1/mcp_utils.py
  • src/backend/base/langflow/api/v1/endpoints.py
  • src/backend/base/langflow/api/utils/core.py
  • src/backend/base/langflow/api/v2/files.py
  • src/backend/base/langflow/api/v1/chat.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
src/backend/base/langflow/api/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)

Backend API endpoints should be organized by version (v1/, v2/) under src/backend/base/langflow/api/ with specific modules for features (chat.py, flows.py, users.py, etc.)

Files:

  • src/backend/base/langflow/api/log_router.py
  • src/backend/base/langflow/api/v1/schemas.py
  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/api/v1/validate.py
  • src/backend/base/langflow/api/v1/users.py
  • src/backend/base/langflow/api/v1/files.py
  • src/backend/base/langflow/api/v1/monitor.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/api/v2/registration.py
  • src/backend/base/langflow/api/v1/projects.py
  • src/backend/base/langflow/api/v1/mcp.py
  • src/backend/base/langflow/api/v1/mcp_utils.py
  • src/backend/base/langflow/api/v1/endpoints.py
  • src/backend/base/langflow/api/utils/core.py
  • src/backend/base/langflow/api/v2/files.py
  • src/backend/base/langflow/api/v1/chat.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
{pyproject.toml,uv.lock}

📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)

Use uv (>=0.4) for Python dependency management in backend development

Files:

  • pyproject.toml
🧠 Learnings (14)
📚 Learning: 2025-09-21T09:46:44.276Z
Learnt from: iann0036
Repo: langflow-ai/langflow PR: 9935
File: src/lfx/src/lfx/components/amazon/aws_api_call.py:35-48
Timestamp: 2025-09-21T09:46:44.276Z
Learning: Amazon components in the Langflow bundle consistently require explicit AWS credentials (aws_access_key_id and aws_secret_access_key with required=True) rather than falling back to environment/machine credentials for security reasons in hosted environments.

Applied to files:

  • .env.example
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to src/backend/base/langflow/components/**/__init__.py : Update `__init__.py` with alphabetically sorted imports when adding new components

Applied to files:

  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/helpers/flow.py
  • src/backend/base/langflow/initial_setup/starter_projects/Custom Component Generator.json
📚 Learning: 2025-07-23T21:19:22.567Z
Learnt from: deon-sanchez
Repo: langflow-ai/langflow PR: 9158
File: src/backend/base/langflow/api/v1/mcp_projects.py:404-404
Timestamp: 2025-07-23T21:19:22.567Z
Learning: In langflow MCP projects configuration, prefer using dynamically computed URLs (like the `sse_url` variable) over hardcoded localhost URLs to ensure compatibility across different deployment environments.

Applied to files:

  • src/backend/base/langflow/api/utils/mcp/__init__.py
  • src/backend/base/langflow/api/utils/mcp/config_utils.py
  • src/backend/base/langflow/api/v1/projects.py
  • src/backend/base/langflow/api/v1/mcp_projects.py
📚 Learning: 2025-10-29T03:59:53.796Z
Learnt from: ricofurtado
Repo: langflow-ai/langflow PR: 10430
File: src/backend/base/langflow/api/v2/registration.py:0-0
Timestamp: 2025-10-29T03:59:53.796Z
Learning: The user registration functionality in `src/backend/base/langflow/api/v2/registration.py` is temporary and will be deprecated when single-user registration is confirmed as a requirement for the langflow project.

Applied to files:

  • src/backend/base/langflow/api/v1/users.py
  • src/backend/base/langflow/api/v2/registration.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Test Langflow's `Message` objects using `langflow.schema.message.Message` and validate text, sender, sender_name, session_id, files, and properties attributes

Applied to files:

  • src/backend/base/langflow/api/v1/monitor.py
  • src/backend/base/langflow/api/v1/mcp_utils.py
📚 Learning: 2025-11-24T19:47:28.997Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T19:47:28.997Z
Learning: Applies to src/backend/tests/**/*.py : Test Langflow REST API endpoints using the `client` fixture with appropriate HTTP methods (GET, POST, etc.), headers (logged_in_headers), and payload validation

Applied to files:

  • .github/workflows/release_nightly.yml
  • src/backend/base/langflow/api/v1/endpoints.py
📚 Learning: 2025-10-29T04:04:47.426Z
Learnt from: ricofurtado
Repo: langflow-ai/langflow PR: 10430
File: src/backend/base/langflow/api/v2/registration.py:25-26
Timestamp: 2025-10-29T04:04:47.426Z
Learning: The user registration endpoints in `src/backend/base/langflow/api/v2/registration.py` are specifically designed for Desktop environment deployment only, not for server or containerized deployments.

Applied to files:

  • src/backend/base/langflow/api/v2/registration.py
  • src/backend/base/langflow/api/v1/endpoints.py
📚 Learning: 2025-10-29T03:55:50.216Z
Learnt from: ricofurtado
Repo: langflow-ai/langflow PR: 10430
File: src/backend/base/langflow/api/v2/registration.py:0-0
Timestamp: 2025-10-29T03:55:50.216Z
Learning: In the langflow project, user registration endpoints in API v2 (`src/backend/base/langflow/api/v2/registration.py`) are intentionally designed to be unauthenticated to support Desktop application initialization and onboarding flows.

Applied to files:

  • src/backend/base/langflow/api/v2/registration.py
  • src/backend/base/langflow/api/v1/endpoints.py
📚 Learning: 2025-06-26T19:43:18.260Z
Learnt from: ogabrielluiz
Repo: langflow-ai/langflow PR: 0
File: :0-0
Timestamp: 2025-06-26T19:43:18.260Z
Learning: In langflow custom components, the `module_name` parameter is now propagated through template building functions to add module metadata and code hashes to frontend nodes for better component tracking and debugging.

Applied to files:

  • src/backend/base/langflow/initial_setup/starter_projects/Custom Component Generator.json
📚 Learning: 2025-08-07T20:23:23.569Z
Learnt from: edwinjosechittilappilly
Repo: langflow-ai/langflow PR: 0
File: :0-0
Timestamp: 2025-08-07T20:23:23.569Z
Learning: Some Langflow starter project files and components still use `from loguru import logger` instead of the centralized structlog logger from `langflow.logging.logger`. These should be updated to ensure consistent structured logging across the entire codebase.

Applied to files:

  • src/backend/base/langflow/initial_setup/starter_projects/Custom Component Generator.json
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to src/backend/base/langflow/components/**/*.py : Add new components to the appropriate subdirectory under `src/backend/base/langflow/components/` (agents/, data/, embeddings/, input_output/, models/, processing/, prompts/, tools/, or vectorstores/)

Applied to files:

  • src/backend/base/langflow/initial_setup/starter_projects/Custom Component Generator.json
  • src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json
📚 Learning: 2025-08-07T20:23:23.569Z
Learnt from: edwinjosechittilappilly
Repo: langflow-ai/langflow PR: 0
File: :0-0
Timestamp: 2025-08-07T20:23:23.569Z
Learning: The Langflow codebase has an excellent structlog implementation that follows best practices, with proper global configuration, environment-based output formatting, and widespread adoption across components. The main cleanup needed is updating starter project templates and documentation examples that still contain legacy `from loguru import logger` imports.

Applied to files:

  • src/backend/base/langflow/initial_setup/starter_projects/Custom Component Generator.json
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to {pyproject.toml,uv.lock} : Use `uv` (>=0.4) for Python dependency management in backend development

Applied to files:

  • .github/workflows/cross-platform-test.yml
📚 Learning: 2025-11-24T19:46:09.104Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/backend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:09.104Z
Learning: Applies to src/backend/base/langflow/api/**/*.py : Backend API endpoints should be organized by version (v1/, v2/) under `src/backend/base/langflow/api/` with specific modules for features (chat.py, flows.py, users.py, etc.)

Applied to files:

  • src/backend/base/langflow/api/v1/endpoints.py
  • src/backend/base/langflow/api/v1/chat.py
🧬 Code graph analysis (11)
src/backend/base/langflow/api/log_router.py (1)
src/backend/base/langflow/services/auth/utils.py (1)
  • get_current_active_user (261-264)
src/backend/base/langflow/api/utils/mcp/__init__.py (1)
src/backend/base/langflow/api/utils/mcp/config_utils.py (4)
  • get_composer_streamable_http_url (256-265)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
  • get_url_by_os (181-204)
src/backend/base/langflow/api/v1/validate.py (1)
src/backend/base/langflow/services/auth/utils.py (1)
  • get_current_active_user (261-264)
src/backend/base/langflow/api/v1/users.py (2)
src/backend/tests/unit/api/v2/test_mcp_servers_file.py (1)
  • current_user (87-91)
src/backend/base/langflow/services/auth/utils.py (1)
  • get_current_active_superuser (267-272)
src/backend/base/langflow/api/v1/monitor.py (4)
src/backend/base/langflow/services/database/models/flow/model.py (1)
  • Flow (186-213)
src/backend/base/langflow/services/database/models/message/model.py (1)
  • MessageTable (126-171)
src/backend/base/langflow/services/database/models/transactions/crud.py (1)
  • transform_transaction_table (77-82)
src/backend/base/langflow/services/auth/utils.py (1)
  • get_current_active_user (261-264)
src/backend/base/langflow/helpers/flow.py (1)
src/backend/base/langflow/services/database/models/flow/model.py (2)
  • Flow (186-213)
  • to_data (198-208)
src/backend/base/langflow/api/v2/registration.py (1)
src/backend/base/langflow/services/auth/utils.py (1)
  • get_current_active_user (261-264)
src/backend/base/langflow/api/v1/projects.py (2)
src/backend/base/langflow/api/utils/mcp/config_utils.py (2)
  • get_project_sse_url (233-238)
  • get_project_streamable_http_url (225-230)
src/backend/base/langflow/api/v1/mcp_projects.py (1)
  • register_project_with_composer (1378-1410)
src/backend/base/langflow/api/v1/endpoints.py (3)
src/backend/base/langflow/services/deps.py (1)
  • get_settings_service (125-138)
src/backend/base/langflow/services/auth/utils.py (1)
  • get_current_active_user (261-264)
src/backend/base/langflow/api/v1/schemas.py (1)
  • ConfigResponse (362-412)
src/backend/base/langflow/api/v2/files.py (3)
src/backend/base/langflow/services/storage/service.py (2)
  • get_file_size (82-83)
  • get_file_stream (61-75)
src/lfx/src/lfx/services/storage/local.py (1)
  • get_file_size (188-213)
src/lfx/src/lfx/base/data/storage_utils.py (1)
  • get_file_size (139-174)
src/backend/base/langflow/api/v1/chat.py (5)
src/backend/base/langflow/services/auth/utils.py (1)
  • get_current_active_user (261-264)
src/backend/base/langflow/services/deps.py (1)
  • get_queue_service (242-246)
src/backend/base/langflow/api/utils/core.py (1)
  • EventDeliveryType (44-47)
src/backend/base/langflow/api/build.py (1)
  • get_flow_events_response (101-164)
src/backend/base/langflow/api/v1/schemas.py (1)
  • CancelFlowResponse (415-419)
🪛 Gitleaks (8.30.0)
.secrets.baseline

[high] 762-762: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 770-770: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 780-780: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 788-788: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 798-798: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 806-806: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 816-816: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 824-824: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 834-834: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 844-844: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 852-852: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 860-860: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 868-868: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment thread pyproject.toml Outdated
Comment on lines +138 to +139
"cuga~=0.1.11",
"agent-lifecycle-toolkit~=0.4.4",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

cuga 0.1.11 PyPI package

💡 Result:

There is no cuga 0.1.11 on PyPI. The latest published release on PyPI is 0.1.7 (uploaded Nov 14, 2025). See the package page for release history. [1]

If you need me to check a different index or the project’s GitHub for unreleased tags, I can do that. [1]


🌐 Web query:

agent-lifecycle-toolkit 0.4.4 PyPI package

💡 Result:

I couldn't find a PyPI release for agent-lifecycle-toolkit version 0.4.4. The project's PyPI release history shows 0.4.0, 0.4.1 and 0.4.2 (and later 0.5.0+), but no 0.4.4 entry. [1][2]

Sources:

  • agent-lifecycle-toolkit PyPI project page (release history). [1]
  • agent-lifecycle-toolkit PyPI specific release page (0.4.1 / 0.4.2 shown). [2]

Dependency versions do not exist on PyPI and will cause installation failures.

The specified versions cannot be installed:

  • cuga 0.1.11 does not exist on PyPI (latest available is 0.1.7)
  • agent-lifecycle-toolkit 0.4.4 does not exist on PyPI (available versions: 0.4.0, 0.4.1, 0.4.2, then 0.5.0+)

Update these to existing versions available on PyPI, or if these versions are intentionally unpublished, clarify the dependency source.

🤖 Prompt for AI Agents
In pyproject.toml around lines 138-139, the pinned versions "cuga~=0.1.11" and
"agent-lifecycle-toolkit~=0.4.4" do not exist on PyPI and will break installs;
change them to valid published releases (e.g., "cuga~=0.1.7" and
"agent-lifecycle-toolkit~=0.4.2" or to "agent-lifecycle-toolkit~=0.5.0" if you
need the newer series), or if those exact unpublished versions are required,
replace the entries with a valid VCS or private index source (add the repository
URL or --extra-index-url) and update the project documentation to explain the
non-PyPI source.

Comment on lines +241 to +265
async def _get_mcp_composer_auth_config(project: Folder) -> dict:
"""Decrypt and return MCP Composer auth configuration for a project."""
auth_config = None
if project.auth_settings:
decrypted_settings = decrypt_auth_settings(project.auth_settings)
if decrypted_settings:
auth_config = decrypted_settings

if not auth_config:
error_message = "Auth config is missing. Please check your settings and try again."
raise ValueError(error_message)

return auth_config


async def get_composer_streamable_http_url(project: Folder) -> str:
"""Generate Streamable HTTP URL for the MCP Composer instance."""
auth_config = await _get_mcp_composer_auth_config(project)
composer_host = auth_config.get("oauth_host")
composer_port = auth_config.get("oauth_port")
if not composer_host or not composer_port:
error_msg = "OAuth host and port are required to get the MCP Composer URL"
raise ValueError(error_msg)
composer_url = f"http://{composer_host}:{composer_port}"
return await get_url_by_os(composer_host, int(composer_port), composer_url) # type: ignore[arg-type]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add error handling for invalid port values.

The get_composer_streamable_http_url function converts composer_port to int at line 265, but if the decrypted config contains an invalid value (e.g., non-numeric string), this will raise a ValueError that propagates as a 500 error.

Consider adding validation:

     composer_host = auth_config.get("oauth_host")
     composer_port = auth_config.get("oauth_port")
     if not composer_host or not composer_port:
         error_msg = "OAuth host and port are required to get the MCP Composer URL"
         raise ValueError(error_msg)
+    try:
+        port = int(composer_port)
+    except (ValueError, TypeError) as e:
+        raise ValueError(f"Invalid oauth_port value: {composer_port}") from e
     composer_url = f"http://{composer_host}:{composer_port}"
-    return await get_url_by_os(composer_host, int(composer_port), composer_url)
+    return await get_url_by_os(composer_host, port, composer_url)

Comment on lines +593 to 605
# Provide direct connection URLs since we're no longer using composer
streamable_http_url = await get_project_streamable_http_url(project_id)
legacy_sse_url = await get_project_sse_url(project_id)
if not streamable_http_url:
raise HTTPException(status_code=500, detail="Failed to get direct Streamable HTTP URL")

response["result"] = {
"project_id": str(project_id),
"sse_url": sse_url,
"streamable_http_url": streamable_http_url,
"legacy_sse_url": legacy_sse_url,
"sse_url": legacy_sse_url,
"uses_composer": False,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent null check for URLs.

streamable_http_url is validated but legacy_sse_url is not. If both URLs are required for the response, validate both consistently.

                     # Provide direct connection URLs since we're no longer using composer
                     streamable_http_url = await get_project_streamable_http_url(project_id)
                     legacy_sse_url = await get_project_sse_url(project_id)
-                    if not streamable_http_url:
-                        raise HTTPException(status_code=500, detail="Failed to get direct Streamable HTTP URL")
+                    if not streamable_http_url or not legacy_sse_url:
+                        raise HTTPException(status_code=500, detail="Failed to get direct connection URLs")
🤖 Prompt for AI Agents
In src/backend/base/langflow/api/v1/mcp_projects.py around lines 593 to 605, the
code validates streamable_http_url but not legacy_sse_url leading to
inconsistent null handling; add a check for legacy_sse_url similar to
streamable_http_url (e.g., if not legacy_sse_url: raise
HTTPException(status_code=500, detail="Failed to get legacy SSE URL")), or
adjust logic to allow one missing URL by documenting and setting the response
field to None accordingly, then ensure response["result"] includes the validated
values.

Comment on lines +135 to +152
if current_user:
user_files_stmt = select(UserFile).where(UserFile.user_id == current_user.id)
user_files = (await session.exec(user_files_stmt)).all()
for user_file in user_files:
stored_path = getattr(user_file, "path", "") or ""
stored_filename = Path(stored_path).name if stored_path else user_file.name
safe_filename = quote(stored_filename)
if stored_filename.startswith(f"{MCP_SERVERS_FILE}_{current_user.id}"):
# reserved file name for langflow MCP server config file(s)
continue
description = getattr(user_file, "provider", None) or "User file uploaded via File Manager"
resource = types.Resource(
uri=f"{base_url}/api/v1/files/download/{current_user.id}/{safe_filename}",
name=stored_filename,
description=description,
mimeType=build_content_type_from_extension(stored_filename),
)
resources.append(resource)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the UserFile model definition
find . -name "*.py" -type f -exec grep -l "class UserFile" {} \;

Repository: langflow-ai/langflow

Length of output: 46


🏁 Script executed:

#!/bin/bash
# Search for UserFile class definition
rg "class UserFile" -A 50 --type py

Repository: langflow-ai/langflow

Length of output: 46


🏁 Script executed:

#!/bin/bash
# Search for UserFile in the entire codebase
rg "UserFile" --type py | head -30

Repository: langflow-ai/langflow

Length of output: 3036


🏁 Script executed:

#!/bin/bash
# Search for imports of UserFile
rg "from.*import.*UserFile|import.*UserFile" --type py

Repository: langflow-ai/langflow

Length of output: 1314


🏁 Script executed:

#!/bin/bash
# Find and read the File model
find . -path "*/langflow/services/database/models/file/model.py" -type f

Repository: langflow-ai/langflow

Length of output: 130


🏁 Script executed:

#!/bin/bash
# Read the File model
cat src/backend/base/langflow/services/database/models/file/model.py

Repository: langflow-ai/langflow

Length of output: 803


Remove unnecessary getattr() for path—it's a required field in the File model.

The path field is defined as path: str = Field(nullable=False) (required), so the getattr(user_file, "path", "") pattern is unnecessary and suggests false uncertainty about the model schema. Access it directly as user_file.path. The getattr(user_file, "provider", None) is appropriate since provider is optional (str | None), and user_file.name is correctly accessed directly since name is also required.

🤖 Prompt for AI Agents
In src/backend/base/langflow/api/v1/mcp_utils.py around lines 135 to 152, remove
the unnecessary use of getattr(user_file, "path", "") since path is a required
non-nullable field on the File model; access user_file.path directly and compute
stored_filename from Path(user_file.path).name (falling back to user_file.name
only if needed), keep getattr(user_file, "provider", None) for the optional
provider field, and ensure safe_filename, URI construction, and mimeType logic
remain unchanged.



@router.get("/builds")
@router.get("/builds", dependencies=[Depends(get_current_active_user)])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Good security improvement: Authentication added to /builds endpoints.

These endpoints were previously unprotected and now require an authenticated user. However, the authentication dependency only verifies the user is logged in but doesn't filter the results by user ownership. The flow_id parameter comes from the client, so a user could potentially access or delete vertex builds for flows they don't own.

Consider adding user-scoped filtering similar to get_messages and get_message_sessions:

 @router.get("/builds", dependencies=[Depends(get_current_active_user)])
-async def get_vertex_builds(flow_id: Annotated[UUID, Query()], session: DbSession) -> VertexBuildMapModel:
+async def get_vertex_builds(
+    flow_id: Annotated[UUID, Query()],
+    session: DbSession,
+    current_user: Annotated[User, Depends(get_current_active_user)],
+) -> VertexBuildMapModel:
     try:
+        # Verify flow belongs to current user
+        flow = await session.get(Flow, flow_id)
+        if not flow or flow.user_id != current_user.id:
+            raise HTTPException(status_code=404, detail="Flow not found")
         vertex_builds = await get_vertex_builds_by_flow_id(session, flow_id)

Also applies to: 36-36

🤖 Prompt for AI Agents
In src/backend/base/langflow/api/v1/monitor.py around lines 27 and 36, the
endpoints were changed to require authentication but still accept a
client-provided flow_id without verifying ownership; update both endpoints to
take the current user (via Depends(get_current_active_user)), check that the
provided flow_id belongs to that user (e.g., query the Flow/Project by id and
compare owner_id/user_id to current_user.id), restrict the builds query to that
owner's flows (or filter by owner_id) and return a 403/raise HTTPException if
the flow is not found or not owned by the user before proceeding with get/delete
logic.



@router.get("/")
@router.get("/", dependencies=[Depends(get_current_active_user)])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/backend/base/langflow/api/v2/registration.py

Repository: langflow-ai/langflow

Length of output: 5758


🏁 Script executed:

rg -i "desktop" src/backend/base/langflow/ -A 2 -B 2 --max-count 20

Repository: langflow-ai/langflow

Length of output: 9134


🏁 Script executed:

rg "get_current_active_user" src/backend/base/langflow/services/auth/ -A 10

Repository: langflow-ai/langflow

Length of output: 2025


🏁 Script executed:

rg "def get_current_user" src/backend/base/langflow/services/auth/ -A 15 -B 2

Repository: langflow-ai/langflow

Length of output: 6186


🏁 Script executed:

rg "skip_auth" src/backend/base/langflow/services/auth/ -A 5 -B 2

Repository: langflow-ai/langflow

Length of output: 3179


🏁 Script executed:

rg "skip_auth_auto_login|AUTO_LOGIN" src/backend/base/langflow/ -B 2 -A 2 --max-count 15

Repository: langflow-ai/langflow

Length of output: 23975


🏁 Script executed:

rg "registration" src/backend/base/langflow/api/v2/ -B 3 -A 3

Repository: langflow-ai/langflow

Length of output: 9132


Confirm Desktop application compatibility with authenticated GET endpoint.

Adding authentication to the GET endpoint creates an asymmetry: POST (register) remains unauthenticated while GET (retrieve registration) now requires authentication. This conflicts with the original design where both endpoints were intentionally unauthenticated for Desktop application initialization.

Before this change, Desktop apps could register without credentials (POST) and check registration status (GET). Now they cannot check status without JWT or API key. Verify this doesn't break Desktop initialization flows or ensure Desktop apps have a documented way to obtain credentials during setup.

🤖 Prompt for AI Agents
In src/backend/base/langflow/api/v2/registration.py around line 139, the GET
endpoint was changed to require authentication which breaks the Desktop app flow
because POST remains unauthenticated; to fix, revert the change so the GET route
is unauthenticated (remove the dependencies=[Depends(get_current_active_user)]
from the @router.get decorator) so GET and POST remain symmetric for Desktop
initialization, add a short code comment noting the Desktop initialization
requirement, and update any docs/tests that assumed the GET endpoint requires
auth.

"lf_version": "1.5.0.post1",
"metadata": {
"code_hash": "f2867efda61f",
"code_hash": "b9d63ae59e8a",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Is https://docs.langflow.org/split-text a valid accessible documentation URL?

💡 Result:

No. The URL https://docs.langflow.org/split-text returns a 404 (not found) and is not accessible. [1] The Split Text documentation exists on the Langflow docs site under a different path (see "Split Text" on the components/processing page). [2]

Sources:


🌐 Web query:

Is langchain_cohere version 0.3.5 available and what are its changes from 0.3.3?

💡 Result:

Yes — langchain_cohere version 0.3.5 is available. Source: PyPI listing and the repo releases. [2][1]

Main changes in 0.3.5 vs 0.3.3 (from the GitHub release notes):

  • Deprecate create_csv_agent. [1]
  • Add JSON mode with_structured_output (new json mode). [1]

Sources:
[1] langchain-ai/langchain-cohere — Releases (0.3.5).
[2] langchain-cohere on PyPI.


Fix broken documentation URL for SplitTextComponent.

The documentation URL https://docs.langflow.org/split-text returns a 404 error and is inaccessible. Update it to point to the correct documentation path on the Langflow docs site (components/processing page). The langchain_cohere version bump to 0.3.5 is valid.

🤖 Prompt for AI Agents
In src/backend/base/langflow/initial_setup/starter_projects/Knowledge
Ingestion.json around line 92, the documentation URL for SplitTextComponent is
incorrect (https://docs.langflow.org/split-text returns 404); update that value
to the correct docs path under the components processing section (e.g.,
https://docs.langflow.org/components/processing or the specific anchor for
SplitText on that page) so the link resolves, and leave the langchain_cohere
version bump to 0.3.5 as-is.

@AntonioABLima AntonioABLima force-pushed the fix/prevent-duplicate-flow-creation branch from c10d986 to 909bdb1 Compare December 15, 2025 03:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant