Commit Graph

17889 Commits

Author SHA1 Message Date
fc1d32a3a2 ref: set wxo ff to true by default (#12826)
* Set wxo ff to true by default

* update test

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Hamza Rashid <74062092+HzaRashid@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
(cherry picked from commit 0b5038cf41)
2026-04-23 17:49:52 -07:00
b0afe3d2d6 fix(security): close IDOR in get_flow_by_id_or_endpoint_name (LE-639) (#12832)
* fix(security): close IDOR in get_flow_by_id_or_endpoint_name (LE-639)

The helper at helpers/flow.py::get_flow_by_id_or_endpoint_name had two
symmetric holes that let an authenticated user resolve another user's
flow by UUID or endpoint_name:

1. UUID branch called ``session.get(Flow, flow_id)`` with zero ownership
   check -- user_id was completely ignored when a UUID was supplied.
2. endpoint_name branch only applied the user_id filter when a truthy
   user_id was passed.  FastAPI ``Depends(get_flow_by_id_or_endpoint_name)``
   resolves user_id as a query parameter that no real caller sets, so the
   filter was silently skipped on every route using the Depends pattern.

Reported exploit path (Jira LE-639): POST /api/v1/responses with the
victim's flow UUID in the ``model`` field executes the victim's flow
and returns the output.  openai_responses.py and v2/workflow.py pass
user_id explicitly, so fixing the helper auto-fixes those endpoints;
the v1 /run* endpoints are defense-in-depth covered by
check_flow_user_permission in _run_flow_internal, but now fail closed
at the helper layer too.

Fix normalizes user_id once at the top of the helper and enforces it on
both branches.  Cross-user lookups return None so the shared 404 path
fires (matches api/v1/files.py::get_flow pattern), which avoids
disclosing flow existence via a 403-vs-404 oracle.  user_id=None
preserves the existing no-scope behavior for webhooks and internal
callers that legitimately need cross-user lookup.

Tests:
- 8 new unit tests on the helper covering same-user UUID, cross-user
  UUID (primary IDOR), UUID instance vs str user_id, no-user-scope,
  missing-flow, endpoint_name with and without user_id.
- 1 new integration test reproducing the Jira PoC on /api/v1/responses:
  attacker with valid API key receives flow_not_found when supplying
  the victim's flow UUID.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(security): apply LE-639 scoping to the /run* Depends routes too

Reviewer pointed out two gaps in the original helper-only fix:

1. Three /run* endpoints still used ``Depends(get_flow_by_id_or_endpoint_name)``
   directly.  FastAPI exposes ``user_id`` as a plain query parameter in that
   shape, which no real caller sets, so the helper stayed unscoped on those
   routes and ownership was only enforced later by check_flow_user_permission
   with a 403.  That leaves a 403-vs-404 flow-existence oracle open.

2. Eager ``UUID(user_id)`` normalization in the helper turned a malformed
   query-string ``?user_id=foo`` into a raw 500.

Fixes:

- Add two auth-aware wrapper dependencies in endpoints.py:
  - get_flow_for_api_key_user: pulls the caller from api_key_security
  - get_flow_for_current_user: pulls the caller from CurrentActiveUser
  Both forward the authenticated user_id to the existing helper.

- Swap the three bare dependencies to the wrappers:
  - /api/v1/run/{flow_id_or_name}
  - /api/v1/run/session/{flow_id_or_name}
  - /api/v1/run/advanced/{flow_id_or_name}
  The webhook and webhook-events routes keep the bare helper: webhook is
  intentionally public and webhook-events already does an explicit
  str(flow.user_id) != str(user.id) check before subscribing.

- In helpers/flow.py, wrap the UUID(user_id) normalization and convert
  ValueError / AttributeError into the same 404 path used for missing
  flows.  Keeps the fail-closed posture -- a caller whose identity we
  can't resolve never learns whether a flow exists.

Tests:

- Update the four existing cross-user tests (/run, /run with payload,
  /run with streaming, /run/advanced) from 403 to 404, and swap the
  "You do not have permission" substring check for a leak-safe
  "'permission' not in response.text.lower()" assertion.  Also update
  test_permission_check_blocks_before_execution for the same reason.
- Add test_user_cannot_run_other_users_flow_session_endpoint for the
  /run/session path (monkeypatches agentic_experience=True and logs in
  as a second local user via session auth).
- Add test_run_rejects_malformed_user_id_query_param asserting that
  ``?user_id=<junk>`` never surfaces as 500 on the /run route.
- Add three parametrized unit tests on the helper covering "not-a-uuid",
  "", and "12345-not-a-real-uuid" -- each must raise 404 without hitting
  the database.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit 2c9f498d66)
2026-04-23 17:49:52 -07:00
f52d0f0072 fix: prevent XSS in chat messages with rehype-sanitize (#12718)
* fix(security): prevent XSS in chat messages with DOMPurify sanitization

- Add DOMPurify sanitization to MarkdownField component
- Sanitize all chat messages before rendering to prevent XSS attacks
- Add comprehensive test suite (21 tests, all passing)
- Block all major XSS vectors: scripts, event handlers, iframes, SVG attacks
- Prevent session theft, cookie exfiltration, and DOM manipulation
- Fixes stored XSS vulnerability in chat message rendering

Security Impact:
- Prevents execution of malicious JavaScript in chat messages
- Protects user session cookies from theft
- Blocks potential RCE chain via XSS vector
- All user input is now sanitized before rendering

Tests verify protection against:
- Script injection (various casings)
- Event handler exploitation (onclick, onerror, onload, onmouseover)
- Iframe-based attacks (srcdoc, javascript: protocol)
- SVG XSS vectors
- Link-based XSS (javascript:, data: protocols)
- Real-world attack scenarios (cookie theft, DOM manipulation, keyloggers)

All 21 security tests passing ✓

* fix: address GitHub Copilot review feedback for XSS security

- Optimize performance: Add useMemo to prevent repeated sanitization on re-renders
- Preserve <think> tags: Implement marker replacement pattern to handle special tags
- Fix playground chat: Apply same XSS protection to playground chat component
- Improve tests: Rewrite tests to directly validate DOMPurify logic with 33 parameterized test cases
- All tests passing (33/33)

Addresses all feedback from GitHub Copilot code review

Note: Pre-existing 'any' types in original code not modified

* fix(frontend): add rehype-sanitize to prevent XSS attacks in chat messages

- Added rehype-sanitize to markdown pipeline after rehypeRaw
- Prevents stored XSS attacks from malicious HTML in chat messages
- Added user-friendly warning banner when HTML content is sanitized
- Preserves code blocks with HTML/JSX syntax
- Updated tests to cover sanitization warning functionality
- All 15 tests passing

Addresses CVE-level XSS vulnerability while maintaining good UX

* fix(frontend): remove any types from test mocks

* feat: add warning message for sanitized content in chat

- Add user-friendly warning when content is filtered by security sanitization
- Detect empty rendered output after rehype-sanitize processing
- Display warning: 'The response was filtered by security sanitization and cannot be displayed'
- Fix sanitizeSchema to handle undefined defaultSchema in test environments
- All 3818 frontend tests passing including 15 XSS security tests

* fix: improve sanitization warning and allow media elements

- Check for img, hr, video, audio elements in addition to text content
- Prevents false warnings when response contains only media or layout elements
- Move editedFlag outside warning conditional to preserve UI state
- Add video and audio tags to sanitization allowlist with safe attributes
- Only allow http/https protocols for media src attributes
- Add test to verify media elements are in sanitization schema
- Addresses code review feedback from Claude

* refactor: extract shared SanitizedMarkdown component

- Create reusable SanitizedMarkdown component to eliminate code duplication
- Consolidate security-sensitive sanitization logic in one place
- Both IOModal and Playground now use the same implementation
- Reduces maintenance burden and ensures consistent security behavior
- Removes 105 net lines of duplicated code
- All 16 XSS security tests still passing
- Addresses code review feedback about duplicated security logic

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
(cherry picked from commit f4a2179bfd)
2026-04-23 17:49:52 -07:00
b5662446bc fix(security): require auth on deprecated /api/v1/upload/{flow_id} (#12831)
* fix(security): require auth on deprecated /api/v1/upload/{flow_id}

The deprecated upload endpoint at v1/endpoints.py::create_upload_file had
no authentication dependency, allowing any unauthenticated caller to write
arbitrary files into a flow's cache folder (folder name == supplied
flow_id).  Filename is a SHA-256 of contents, so this is primarily a
disk-DoS / cross-user bucket-pollution vector rather than direct data
exfiltration, but the endpoint is reachable in production (deprecated=True
+ include_in_schema=False only hides it from docs; the route is still
live).

Reuse the existing get_flow dependency from api/v1/files.py, which
already provides authn (CurrentActiveUser) and authz (flow-ownership
check, returning 404 for both missing flows and cross-user access to
prevent info disclosure) for the non-deprecated twin at
/api/v1/files/upload/{flow_id}.  No new auth primitives needed --
get_flow is already the centralized pattern.

Known downstream impact: the public SDK helper lfx.load.utils.upload()
(re-exported as langflow.load.upload_file) hits this route without
credentials.  Callers will now receive 401/403 until the SDK is updated
to pass auth headers and switch to the non-deprecated
/api/v1/files/upload/{flow_id} route -- tracked as follow-up.

Regression tests cover unauthenticated rejection and authenticated
happy-path on the deprecated route.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(security): enforce size limit + unbreak SDK on deprecated upload

Review feedback on #12831 surfaced two follow-ups to the initial auth
fix:

P1: the SDK helper in lfx/load/utils.py still POSTed to
    /api/v1/upload/{flow_id} with no credentials, so any caller of
    langflow.load.upload_file() would receive 401/403 immediately
    after the patched server ships.  Thread an optional api_key
    through upload() and upload_file(), falling back to the
    LANGFLOW_API_KEY env var, so existing SDK consumers can keep
    working without rewriting against the non-deprecated
    /api/v1/files/upload/{flow_id} route.  No arg reordering --
    api_key is appended as an optional kwarg.

P2: the deprecated endpoint dropped into save_uploaded_file without
    consulting max_file_size_upload.  The non-deprecated twin at
    api/v1/files.py:85-91 enforces a 413 above the limit; mirror that
    here so an authenticated user can't fill disk through this route.

Regression tests:
- tests/unit/api/v1/test_endpoints.py::test_deprecated_upload_enforces_max_file_size
  asserts 413 when max_file_size_upload is monkey-patched to 1 MB and
  the body is 2 MB.
- new src/lfx/tests/unit/load/test_upload.py covers: explicit api_key
  sends the x-api-key header, LANGFLOW_API_KEY env var fallback,
  explicit arg overrides env var, no-key sends no header (so server
  authn failure is the correct signal), upload_file forwards the key,
  and a server 401 surfaces as UploadError.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit 99da6d0587)
2026-04-23 17:49:52 -07:00
8524d49db4 docs: concatenate and merge options for dataframe operations component (#12835)
concat-and-merge-table-component

(cherry picked from commit 919054fc17)
2026-04-23 17:49:52 -07:00
efae69f862 fix: preserve spaces in shareable playground chat input (#12833)
(cherry picked from commit d2bd97549a)
2026-04-23 17:49:52 -07:00
05750a371f fix(traces): serialize enum values for PostgreSQL on trace/span tables (#12820)
SQLAlchemy was sending enum *names* (e.g. "OK", "CHAIN") while the
PostgreSQL enums created in migration 3478f0bd6ccb use the enum *values*
("ok", "chain"), causing silent native-tracing failures with
`invalid input value for enum spanstatus: "OK"` and friends on insert.

Configure explicit sa_column=Column(SQLEnum(..., values_callable=...))
for status, span_type, and span_kind so the persisted label always
matches the migration's enum labels. Also pin the PG enum names
("spanstatus", "spantype", "spankind") so a future auto-generated
revision doesn't drift.

Fixes #12817

(cherry picked from commit 682c6144e4)
2026-04-23 17:49:52 -07:00
4fcc24370f ref: removes retries from wxo api calls (#12770)
* refactor(wxo): remove forward-path retries; keep rollback retries only

Forward create/update operations now fail fast so transient and
non-transient errors surface immediately. Retry/backoff is preserved
exclusively for rollback/cleanup paths. A consolidated TODO in
retry.py tracks the future work to reintroduce retries for transient
5xx/timeout errors on the forward path.

Frontend usePostDeployment hook also disables its mutation retry to
match the backend fail-fast behavior.

* Add retry false to patch deployment

* add retry false on a few other paths

* revert: restore wxo backend retry logic, keep frontend retry:false only

(cherry picked from commit fd198d8d0d)
2026-04-23 17:49:52 -07:00
0ab5f040f8 docs: clarify trace storage when multiple providers are enabled (#12816)
clarify-trace-storage-with-multiple-providers

(cherry picked from commit f1863c512d)
2026-04-23 17:49:52 -07:00
f78830730d docs: wxo feature (#12677)
* deployment-sidebars-and-release-note

* fix-sidebars-name

* manage-deployments

* add-requests

* clarify-endpoint-difference

* improve-anchors

* Apply suggestions from code review

Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Mendon Kissling <59585235+mendonk@users.noreply.github.com>

* clarify-menu

* apply-changes-to-next-and-latest

---------

Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com>
(cherry picked from commit 4898aa8621)
2026-04-23 17:49:52 -07:00
4beab56b6b docs: add multi-model opensearch component (#12799)
add-opensearch-multi-model

(cherry picked from commit 15edfe1fec)
2026-04-23 17:49:52 -07:00
aced13ef79 docs: clarify autosaving and versioned flow saving (#12794)
* add-clarification-to-1.9-and-next

* Apply suggestions from code review

Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com>

---------

Co-authored-by: April I. Murphy <36110273+aimurphy@users.noreply.github.com>
(cherry picked from commit 7fd8469917)
2026-04-23 17:49:52 -07:00
dbc87e7704 fix: db guards for deployments (#12339)
* checkout api handlers

* add missing table

* update to use "version" terminology for flows instead of outdated "history" verbage

* Fix provider account id mapping

* recover a little todo comment

* Add flow version migration and minor exception handling, etc

  1. deployments.py — Added AuthenticationError import + handling (401) in all 9 error handler blocks. Added update_deployment_db import + call to persist name changes to local DB after adapter update succeeds.
  2. flow_version/exceptions.py — Added FlowVersionDeployedError for blocking deletion of deployed versions.
  3. flow_version/crud.py —
    - Added has_deployment_attachments() helper that checks if a flow version has any deployment attachments
    - delete_flow_version_entry() now raises FlowVersionDeployedError if the version is attached to deployments
    - Pruning now excludes deployed versions via a NOT IN subquery on FlowVersionDeplottachment, preventing provider-side snapshot orphaning
  4. flow_version/__init__.py — Exports FlowVersionDeployedError
  5. flow_version.py (API route) — Imported FlowVersionDeployedError, added it to _translate_version_error as 409 Conflict
  6. models/__init__.py — Registered FlowVersionDeploymentAttachment for alembic/SQLModel metadata detection
  7. Migration c0d2ce43b315 — Creates flow_version_deployment_attachment table with all columns, FKs (CASCADE), and indexes. Single head, chains off fc7f696a57bf.

* [autofix.ci] apply automated fixes

* address bugs and inconsistencies

* rename column to "provider_snapshot_id"

* harden deployment API: fail loudly on invalid state instead of silently passing

Replace silent fallbacks, warning logs, and dropped values with hard
failures (422/500/502) across the deployment orchestration layer. Adds
logging to bare exception handlers and wraps materialize_snapshots in
the adapter error handler.

* [autofix.ci] apply automated fixes

* add materialize_snapshots to deployment service protocol

Promotes materialize_snapshots from duck-typed getattr usage to a
first-class method on DeploymentServiceProtocol, BaseDeploymentService,
and the no-op DeploymentService stub. Introduces MaterializeSnapshotsResult
schema for typed return values.

Updates deployments.py to call the method through the protocol instead of
getattr, giving static analysis coverage over the contract.

Documents the snapshot abstraction across BaseFlowArtifact, SnapshotItem,
MaterializeSnapshotsResult, and FlowVersionDeploymentAttachment.provider_snapshot_id
— explaining that snapshots are immutable, provider-owned copies of flow data
with opaque provider-assigned identifiers (e.g. wxO tool ID, K8s ConfigMap
name, S3 key).

* deployment sync: extract helpers, server-side type filter, orphan detection

- Extract _fetch_provider_resource_keys helper for provider validation
- Rename _sync_page_with_provider → _list_deployments_synced
- Match resource keys by provider ID only (not name)
- Restore server-side deployment_type filter with guard against
  false deletions (skip rows whose type doesn't match the filter
  instead of deleting them)
- Add orphan/divergence logging for post-create, post-update,
  post-duplicate DB write failures
- Return 422 on invalid UUID in update remove list (was silently ignored)
- Handle NotImplementedError → 501 from provider adapters
- Convert attachment IntegrityError to ValueError with descriptive message
- Add tests for sync helpers (15 cases)

* rebase on release-1.9.0 and align with lfx/services

* refactor(deployments): align provider mapper routing and WXO update payload mapping

Align deployment mapper resolution with adapter-type/provider-key routing and
refactor PATCH update handling to use mapper resolve/shape contracts end-to-end.
Map Langflow flow_version_id references at the API boundary into provider update
operations for Watsonx bind/unbind/remove paths, with expanded mapper tests.

* patch down-revision

* first pass with formalized boundary rules

* fix(deployments): harden watsonx payload boundary contracts

Enforce fail-fast payload slot parsing for required adapter results, split execution create/status slot contracts, and route execution-create mapping through deployment mappers.
Require watsonx flow artifact source_ref and move update reconciliation output to provider_result to keep mapper/adapter boundaries explicit and typed.

* refactor(deployments): modularize watsonx orchestrate create/update flow

Extract create/update logic into dedicated core modules with shared helpers to tighten deployment boundary contracts.
Align backend/lfx payload schema mapping and expand e2e/unit coverage for response mapping and update schema behavior.

* api impl for wxo-specific create payload

* further refactoring. add rollback of existing tools (undo new app bindings) in the create path

* add todo in execution.py

* refactor(deployments): replace snapshot_id/reference_id with source_ref-correlated tool refs
Introduce WatsonxToolRefBinding to correlate source_ref (flow version id)
with provider tool_id across all operation types. This replaces the prior
reference_id and snapshot_id fields with a unified structure that carries
provenance through create, bind, unbind, and remove_tool operations.
Key changes:
- Flatten API operation payloads: hoist flow_version_id onto operations,
  remove nested WatsonxApiUpdateToolReference wrapper
- Replace tools.existing_ids with inline tool_id_with_ref on bind operations
- Rename WatsonxCreateSnapshotBinding to WatsonxToolRefBinding (input) and
  WatsonxResultToolRefBinding (output, with created flag)
- Add created_app_ids to update results for connection tracking
- Raise HTTPException on contract violations in _to_api_tool_app_bindings
  instead of silently dropping unmappable bindings
- Add schema-level validation for conflicting source_ref on same tool_id
- E2E: cache tool_id→source_ref from create results, use helpers to build
  refs with distinct source_ref vs tool_id values

* Enforce DeploymentType enum and add description column

Make deployment_type a required column backed by a SQLAlchemy
TypeDecorator that validates on write (rejects None and invalid strings)
and coerces to DeploymentType on read. Add nullable description column
to the deployment model and surface it through the API.

Key changes:
- Add _DeploymentTypeColumn TypeDecorator for enum round-trip fidelity
- Make deployment_type non-optional in Deployment model, DeploymentRead,
  CRUD functions, and API layer
- Add description (Text, nullable) to Deployment model and fold its
  migration into the existing c0d2ce43b315 revision
- Remove _resolve_deployment_type helper — TypeDecorator handles coercion
- Remove DeploymentType fallbacks and backward-compat shims from API
  endpoints, base mapper, and watsonx orchestrate mapper
- Document cross-package coupling: DeploymentType is owned by lfx but
  persisted by langflow; member values must never be removed
- Fix pre-existing bug: provider_account_id → deployment_provider_account_id
  in get_deployment_status endpoint

Note: deployment_type is nullable=True at the DB level to satisfy the
EXPAND-phase migration validator; NOT NULL is enforced at the application
layer by the _DeploymentTypeColumn TypeDecorator.

* refactor(deployments): extract route helpers, harden sync and error handling
Move bulk of deployment route logic into mappers/helpers layer to slim
down deployments.py and enforce clearer boundary between routes, mappers,
and adapters (documented in DEPLOYMENT_BOUNDARY_RULES.md).
Key changes:
- Extract ~700 lines from deployments.py into helpers.py (pagination,
  adapter/mapper resolution, attachment management, snapshot sync,
  rollback, response shaping)
- Add read-path snapshot-level sync: get_deployment and
  list_deployments_synced verify provider_snapshot_ids against the
  provider and prune stale attachments, with graceful fallback on error
- Add compensating rollback for create (rollback_provider_create) and
  update (rollback_provider_update) when DB commit fails after provider
  mutation, using mapper-driven payload reconstruction
- Introduce handle_adapter_errors() context manager centralising
  DeploymentServiceError → HTTP status mapping via
  http_status_for_deployment_error; sanitise 500 detail to avoid
  leaking internals
- Add DeploymentNotConfiguredError → 503 mapping
- Add util_snapshot_ids_to_verify and resolve_rollback_update to base
  mapper with WxO overrides for provider-specific snapshot ID extraction
  and put_tools-based rollback payloads
- Add put_tools field to WatsonxDeploymentUpdatePayload for full tool
  list replacement; early-return in build_provider_update_plan and
  validate_operation_references when put_tools is set
- Extract verify_tools_by_ids into core/tools.py helper
- Harden resource_name_prefix with strip_whitespace + min_length=1
- Deduplicate snapshot_ids before provider calls
- Add deterministic order_by(created_at) to attachment CRUD queries
- Add exc_info=True to all best-effort rollback/compensate error logs
- Add session.rollback() in get_deployment snapshot sync error path
- Warn when list_snapshots receives both deployment_ids and snapshot_ids
- Add E2E scenarios for empty snapshot list, mixed snapshot IDs, tools
  endpoint, and deployment re-list after update
Tests:
- Add test_deployment_route_handlers.py covering stale-row delete +
  commit, non-404 adapter errors, handle_adapter_errors wiring,
  snapshot sync (happy path, skip, error fallback), project-scoped
  flow version validation for create and update
- Expand test_deployment_sync.py with snapshot-phase tests, rollback
  tests, pagination guard, and project-scoped validation
- Add deployment_type assertion to response mapping test
- Add DeploymentNotConfiguredError and bare DeploymentServiceError cases
  to exception mapping tests
- Add put_tools schema and update plan tests

* remove pompous performance commentary

* fix(deployments): remove upsert behavior and fail fast on duplicate name

The create_deployment endpoint previously performed a
get_deployment_by_resource_key lookup before inserting the DB row,
silently tolerating duplicates. Since the provider adapter returns a
fresh resource ID on every create, this lookup could never legitimately
match — and if it did, it would mask a data inconsistency bug.

Changes:
- Remove the get-or-create (upsert) pattern; go straight to
  create_deployment_db and let the unique constraint surface conflicts.
- Add deployment_name_exists CRUD function and an early 409 guard so
  duplicate names are rejected before any provider call, avoiding
  costly provider-side rollback for a locally-checkable condition.
- Update existing route-handler tests to reflect the removed lookup.
- Add tests for deployment_name_exists and the 409 duplicate-name path.

* feat: convert provider_key and deployment_type columns to DB-level enums
Replace plain string columns with SQLAlchemy Enum types backed by
Postgres/SQLite enum constraints, enforcing valid values at the DB
layer rather than only in application code.
Migration follows expand-contract pattern (add enum column, backfill,
drop old string column, rename) with index ops outside batch context
to avoid SQLite column-lookup issues. Upgrade and downgrade are fully
atomic.
- Add DeploymentProviderKey enum as single source of truth for
  provider identifiers; remove magic strings and _DeploymentTypeColumn
  TypeDecorator
- Make provider_key immutable after creation (remove from update
  request schema and API handler)
- Fix pre-existing test gap: add missing deployment_type argument to
  all TestDeploymentCRUD create_deployment() calls

* feat(flow-versions): add deployment awareness and sync-on-read

Add is_deployed field to flow version responses and provider-verified
sync to the list/get read paths, matching the existing deployment
endpoint sync pattern.

API changes:
- list_flow_versions returns is_deployed per entry and accepts optional
  deployment_ids filter to scope by specific deployments
- get_single_flow_version returns is_deployed on the full response
- Both endpoints now use write sessions and run best-effort
  snapshot-level sync before returning results

Sync-on-read (helpers.sync_flow_version_attachments):
- Queries all attachments for a flow's versions joined with deployment
  and provider account info in a single query
- Groups by provider, resolves adapter/mapper per group, verifies
  provider_snapshot_ids via list_snapshots, and prunes stale attachment
  rows through the existing sync_attachment_snapshot_ids helper
- Per-provider error handling so one provider outage doesn't block the
  response

New CRUD: list_attachments_for_flow_with_provider_info joins
FlowVersionDeploymentAttachment -> Deployment -> DeploymentProviderAccount
to avoid N+1 queries during sync grouping.

Tests: 50 passing (11 new) covering is_deployed indicator, deployment_ids
filtering, stale attachment pruning on list/get, and sync failure
resilience.

* Add user id authentication to a few missing endpoints

* [autofix.ci] apply automated fixes

* Update tests

Removes some mock tests that did nothing
Adds sqlite for true testing of db accessors
Reduces Mock objects usage

* refactor(deployments): enforce ownership boundaries on execution responses
Move provider-owned execution identifiers (execution_id, agent_id,
status, timestamps, errors) out of the top-level API response and into
provider_data, keeping only Langflow-owned fields (deployment_id) at the
top level.  This prevents future collisions if Langflow introduces its
own execution tracking.
Key changes:
- Remove execution_id from _ExecutionResponseBase; provider's opaque
  run identifier now lives exclusively inside provider_data
- Rename WatsonxExecutionResultData → WatsonxAgentExecutionResultData
  (adapter layer) and split the API-layer class into a private base
  (_WatsonxApiAgentExecutionResultBase) with dedicated
  WatsonxApiAgentExecutionCreateResultData and
  WatsonxApiAgentExecutionStatusResultData subclasses
- Translate WXO run_id → execution_id at the adapter boundary
  (create_agent_run_result / get_agent_run).
- Collapse util_execution_id + util_execution_deployment_resource_key
  into a single util_resource_key_from_execution that trusts the
  adapter-provided result.deployment_id directly
- Remove build_orchestrate_runs_query and extra payload fields
  (thread_id, llm_params, guardrails, etc.) unused in MVP
- Simplify WxOClient.post_run signature (drop query_suffix)
- Exclude provider_data from flow tool artifact to avoid unexpected
  top-level keys in the WxO tool runtime
- Document ownership boundary rules in DEPLOYMENT_BOUNDARY_RULES.md §14
- Add E2E polling for terminal execution status, input format variants,
  and missing-deployment negative test
- Expand unit tests for renamed schemas, field mapping, passthrough
  validation, and simplified payload builder

* feat: add name column to deployment_provider_account
Add a required, user-chosen display name to provider accounts
(e.g. "staging", "prod") that is unique within a given provider_key.
Includes model, CRUD, API schema/route, mapper, migration with
backfill, and tests.

* fix: replace hand-written migration with Alembic-generated revision
Replace the manually authored migration (b4e6f8a2c1d3) with an
Alembic-generated one (8255e9fc18d9) for the deployment_provider_account
name column.
Fix SQLite compatibility: sa.func.concat() generates a concat() function
call which does not exist in SQLite. Use sa.literal().concat() instead,
which produces the || operator and works on both PostgreSQL and SQLite.

* remove bogus unverified math from migration file

* feat: verify provider credentials before account creation
Add a verify_credentials step to the provider account creation flow
that validates API keys against the provider before persisting them.
This prevents storing invalid or revoked credentials and gives users
immediate feedback.
Key changes:
- Add verify_credentials to the deployment adapter interface (base,
  service, protocol) with WXO implementation that obtains a token
  via the IBM authenticator
- Add SSRF-hardened URL validation for provider_url (HTTPS-only,
  private IP blocklist, localhost rejection, normalization)
- Introduce ValidatedUrl/ValidatedUrlOptional annotated types in
  the API schema layer
- Refactor raise_for_status_and_detail to accept an optional cause
  parameter for explicit exception chain control
- Use ResourceNotFoundError (parent) instead of DeploymentNotFoundError
  in raise_for_status_and_detail for provider-agnostic 404 mapping
- Narrow get_authenticator return type to concrete union

* use whitelist only for valid urls

* feat: BREAKING: move credentials into provider_data and centralize update logic in mapper
- Replace top-level `api_key: SecretStr` with opaque `provider_data: dict` in API schemas;
  mapper extracts credentials via `resolve_credential_fields` for DB storage
- Add `resolve_provider_account_update` to base mapper so routes delegate
  full update-kwargs assembly (including cross-field logic) to the mapper
- WXO mapper override re-derives `provider_tenant_id` when `provider_url` changes
- Add tenant extraction utilities and `validate_tenant_url_consistency` in
  `deployment_provider_account/utils.py` as single source of truth
- Add `model_validator` on `DeploymentProviderAccount` for defense-in-depth
  tenant/URL consistency checks
- Rename `DEPLOYMENT_BOUNDARY_RULES.md` → `RULES.md`; document DB-direction
  mapper contract, credential flow, and update assembly
- Update all tests for new `provider_data` shape, mapper update methods,
  tenant extraction, and model-level consistency validation

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* fix(deployments): Harden provider account validation and WXO rollback

Use provider_url when resolving WXO credentials and scope provider account names per user within each provider. Re-verify provider account updates before persisting them and return 4xx responses for account conflicts instead of surfacing 500s.

Block deleting provider accounts that still own deployments and extend create rollback so failed WXO writes clean up provider-side resources. Add route, schema, mapper, sync, CRUD, and WXO tests to cover the new behavior.

* [autofix.ci] apply automated fixes

* update url validator docs; remove outdated reference to private url blacklist logic

* fix(deployments): Isolate snapshot sync writes

Use nested transactions around best-effort snapshot cleanup so provider sync failures cannot leak partial attachment deletes into the outer request transaction.

Persist explicit description clears during deployment PATCH requests, and add regression tests for both deployment sync paths and the route handler update flow.

* fix(deployments): Harden delete cleanup and wxO create tests

Treat missing provider agents as stale local cleanup so delete can
finish instead of leaving orphaned deployment rows behind.

Commit local delete operations eagerly, retry once on commit failure,
and move wxO create-path tests toward fake client objects so the real
service logic is exercised without external calls.

* new head

* fix(deployments): stop prefixing wxo raw connection app_ids
Preserve caller app_ids for newly created wxo connections while keeping lf_ prefixing for tool/deployment naming, centralize resource_name_prefix validation, and update mapper/service schema tests and docs to reflect the new behavior.

* fix(deployments): Harden provider account cleanup

Reconcile stale deployment rows before provider-account deletion so
out-of-band provider removals do not leave account cleanup blocked.

* fix: restore py310 compatibility and align payload slot tests
Import Self from typing_extensions in the deployment provider account model to keep backend imports working on Python 3.10, and update payload formalization tests to assert strict rejection of cross-model BaseModel inputs.

* fix(deployments): resolve mypy typing regressions across deployment flows
Normalize SQLModel query expressions for typed SQL operators, make deployment-related model IDs non-nullable where appropriate, and tighten provider mapper/update typing guards. Update deployment tests to align with stricter type contracts.

* fix(deployments): restore py310 test compatibility and migration idempotency
Replace Python 3.11-only UTC imports with timezone.utc in deployment-related tests and the watsonx e2e helper, and make the provider-account name migration safe to re-run when the column and unique constraint are created in separate steps.

* fix(deployments): restore provider account route ownership helpers
Reuse the shared provider-account lookup in update/delete routes so route tests keep the expected ownership path, and use the explicit-field helper when deciding whether to reverify credentials.

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* fix(deployments): enforce deployment guard constraints with clean API errors
Add DB trigger guards to block deleting deployed flow versions, deleting projects with deployments, and moving deployed flows across projects. Translate trigger violations to HTTP 409 with sanitized details, add best-effort pre-operation deployment/snapshot sync to reduce stale-state false positives, and cover guard parsing/delete flush behavior with tests.

* checkout latest crud and helpers files

* make helper for try catch sync logic

* add gaurd to prevent moving deployments across projects

* prevent moving deployments across provider accounts

* add live sqlite tests

* recover lost changes

* recover feature flag setup from release-1.9.0

* fix: address review findings for deployment guard branch
- Add comment in delete_user explaining why best-effort provider sync
  is intentionally skipped (too expensive; DB trigger is authoritative)
- Add docstring to _clean_guard_detail for maintainability
- Add happy-path trigger tests verifying operations succeed when no
  deployment conflicts exist (flow version delete, project delete,
  flow move, deployment rename)
- Fix list_attachments_for_flow_with_provider_info callers still using
  the old flow_id= parameter (now flow_ids=)
- Fix pre-existing broken test fixture (missing name in
  DeploymentProviderAccount)

* feat: add cross-project attachment guard and harden test suite
Add a new DB trigger (prevent_cross_project_attachment) that blocks
attaching a flow version to a deployment in a different project, for
both PostgreSQL and SQLite.
Refactor trigger tests into a unified dual-dialect test file
(test_deployment_guard_triggers.py) that runs against both SQLite
(always) and PostgreSQL (when TEST_DEPLOYMENT_GUARD_PG_URL is set).
Validated with both asyncpg and psycopg drivers.
Additional improvements:
- test_downgrade_removes_triggers now asserts the guard is active
  before downgrade, preventing false-positive passes
- Endpoint test uses statement inspection instead of fragile
  call-count gating
- Add exception parser coverage for CROSS_PROJECT_ATTACHMENT,
  implicit __context__ chains, and cyclic exception chains
- Add endpoint tests for cascade_delete_flow, update_flow,
  delete_multiple_flows, and global exception handler

* fix(deployments): make provider account identity immutable
Add DB deployment guard triggers to prevent updating provider account identity fields
(provider_key, provider_tenant_id, provider_url) once created, and preserve guard
error translation in the provider-account PATCH route. Extend trigger and route
tests to cover blocked updates and same-value no-op updates.

* refactor: extract sync logic into dedicated module and fix sync correctness issues
- Move deployment/attachment sync functions from helpers.py to new sync.py
  module, reducing helpers.py size and improving separation of concerns
- Fix fetch_provider_resource_keys to use deployment_ids instead of
  provider_params (pre-existing bug: wrong adapter parameter)
- Add empty-list guard to fetch_provider_resource_keys to avoid
  unnecessary provider calls
- Wrap snapshot sync in savepoint for proper fault isolation
- Update imports across flows.py, projects.py, flow_version.py,
  deployments.py to reference new sync module
- Update unit tests to target refactored module paths and add coverage
  for bug fixes

* refactor: implement binding-aware deployment sync and optimistic guard retries
Add provider binding contract and mapper extraction path:
- add ProviderSnapshotBinding in deployments/contracts.py
- add BaseDeploymentMapper.extract_snapshot_bindings() default no-op
- implement Watsonx mapper extract_snapshot_bindings() with strict provider_data parsing via PayloadSlot (snapshot_ids required)
Replace attachment snapshot verification loop with binding-aware set-based cleanup:
- add delete_unbound_attachments(user_id, deployment_ids, bindings) in flow_version_deployment_attachment/crud.py
  - uses CTE/anti-join to delete local attachments not present in provider bindings
  - when bindings are empty, delete all attachments for provided deployment_ids
- add count_attachments_by_deployment_ids() grouped recount helper
- update list_deployments_synced() in deployments/helpers.py to:
  - collect bindings from fetch_provider_resource_keys() provider_view during batch sync
  - run one savepoint-wrapped delete_unbound_attachments() for accepted deployments
  - run one grouped recount and patch attached_count from DB
  - remove list_attachments_by_deployment_ids + snapshot-provider phase
  - enforce two sync rounds max (initial pass + one refill)
Consolidate provider-side sync plumbing:
- change fetch_provider_resource_keys() in deployments/sync.py to return (known_resource_keys, provider_view)
- document tuple contents in function docstring
- remove now-unused sync_provider_attachment_snapshots() helper
- keep fetch_provider_snapshot_keys()/sync_attachment_snapshot_ids() for single-deployment read path
Scope read sync by provider account and conditionally expose deployment status:
- update deployment CRUD listing helpers to accept provider_account_id filter
- in flow_version API, add deployment_provider_account_id query param
- only perform deployment sync/is_deployed calculation when provider id is explicitly passed
- make FlowVersion.is_deployed optional (default None) so field can be omitted when not requested
Switch mutating APIs to optimistic catch-sync-retry pattern:
- add _retry_on_deployment_guard() in flows.py and projects.py
- replace pre-sync mutation flow with guarded retry on DeploymentGuardError
- for project create/update, retry only guarded flow-move DB operations
- preserve side-effect safety and improve stale-state recovery behavior
Tests:
- add unit tests for deployment guard retry helper (test_deployment_guard_retry.py)
- add unit tests for delete_unbound_attachments behavior, including empty bindings delete-all case (test_flow_version_deployment_attachment_crud.py)
- update deployment sync tests for tuple return, binding extraction, consolidated phase-2 cleanup/recount, and two-round refill behavior
- update flow_version tests for provider-scoped sync and conditional is_deployed field presence

* fix: extract tool_ids from provider data

* dont use stale rolled-back OORM Flow instance

* refactor(deployment): demote delete triggers and standardize guard handling
- Edit the existing deployment guard migration to remove DB delete triggers for flow version and project deletion in both PostgreSQL and SQLite.
- Keep core DB invariants (flow move, deployment project move, deployment provider account move, provider account identity immutability, and cross-project attachment).
- Add a new DB invariant trigger to enforce deployment.resource_key immutability.
- Rewrite trigger error text to technical/operator-focused language and remove ambiguous “detach” wording.
- Add migration comments documenting the current global provider identity guard and the future provider-specific migration path.
- Add app-level guard helpers in deployment/guards.py:
  - check_flow_has_deployed_versions
  - check_project_has_deployments
- Wire check_flow_has_deployed_versions into cascade_delete_flow before destructive deletes.
- Wire check_project_has_deployments into delete_project before deleting the folder row.
- Keep explicit DeploymentGuardError passthrough in cascade_delete_flow and retain parse fallback for non-guard exceptions.
- Refactor DeploymentGuardError to carry:
  - code
  - technical_detail
  - detail (API-friendly message)
- Add friendly guard-detail mapping by guard code.
- Update parse_deployment_guard_error to parse DEPLOYMENT_GUARD:<CODE>:<DETAIL> and preserve raw technical detail from chained exceptions.
- Simplify delete_user to DB-cascade delete + flush only.
- Remove deployment guard translation/parsing logic from delete_user.
- Keep an explicit endpoint comment documenting the intentional no-provider-teardown trade-off.
- Update flow_version deletion message to: “Remove its deployment attachment rows first.”
- Update tests to match the new guard contract and behavior:
  - Remove stale delete_user guard-translation endpoint test.
  - Update delete endpoint tests to assert app-level guard behavior and new friendly details.
  - Update trigger tests to reflect removed delete triggers and added resource_key trigger coverage.
  - Update trigger downgrade assertions for the revised trigger set.
  - Expand exception tests for code mapping, technical-detail preservation, unknown-code fallback, and new guard codes.
  - Add unit tests for deployment guard helper functions.

* surface better msg when project deletion attempted

* refactor(deployment): rename flow guard code and align guard messages
Rename FLOW_VERSION_DEPLOYED to FLOW_HAS_DEPLOYED_VERSIONS across deployment guard checks, project-level remapping, and tests.
Update friendly guard wording for flow/project/move/cross-project cases, and convert pytest match patterns to raw escaped regex strings to satisfy Ruff.

* add docs to migration file

* update docs in migration

* update docs

* refactor(flow-version): scope deployment status to list endpoint
Keep deployment status provider-scoped on version listing only, remove single-version sync/status behavior, and omit is_deployed from snapshot creation responses while aligning docs and tests.

* refactor(deployment): centralize guard retry helpers and align sync error handling
Move flow/project deployment-guard retry wrappers into deployment sync helpers while preserving upstream route logic, and align provider sync error mapping with deployment domain exceptions. Tighten wxo snapshot binding validation and update docs/tests for guard messaging and sync behavior.

* refactor(deployment): enforce snapshot ID invariants and improve delete error UX
- add shared non-empty string guard (`require_non_empty`) in database utils and use it in deployment attachment CRUD write paths
- enforce required `provider_snapshot_id` on attachment model and add Pydantic + SQLAlchemy validators as safety nets for non-CRUD writes
- introduce deployment attachment key schemas (`DeploymentAttachmentKey`, `DeploymentAttachmentKeyBatch`) with deduplication support
- add batch stale-row cleanup (`delete_deployment_attachments_by_keys`) using a VALUES CTE to delete exact deployment/flow-version pairs
- remove mapper-level `util_snapshot_ids_to_verify` hook from base + watsonx mapper and replace with sync utilities:
  - `extract_verified_snapshot_ids`
  - `extract_verified_provider_snapshot_ids`
- update sync and helper flows to use verified snapshot IDs, stricter provider ID validation, and safer corrected-count handling
- align provider ID handling so both deployment and snapshot sync paths reject blank provider IDs consistently
- tighten adapter/provider-key validation and enforce required snapshot binding during attach flow-version operations
- update deployment routes/helpers to new sync signatures and behavior
- remove legacy null/blank snapshot safety-net filters from attachment queries/delete-unbound reconciliation (intentional invariant enforcement)
- add broad backend test coverage for:
  - rejecting empty/blank snapshot IDs on create/update
  - exact-key batch deletion behavior
  - cartesian cross-delete regression prevention
  - updated route/sync expectations after helper refactor
- improve flow delete UX in the frontend list view by surfacing backend `detail` via `getAxiosErrorMessage` instead of a generic "Please try again"

* update logic and remove stale comment

* fix(deployment): handle orphaned deployment attachments and enforce SQLite FK constraints
Enable PRAGMA foreign_keys=ON for SQLite to enforce referential integrity
that was previously silently ignored, and add application-level cleanup for
orphaned FlowVersionDeploymentAttachment rows left behind by environments
where cascades never fired.
- Enable foreign_keys pragma in lfx SQLite settings
- Update deployment guards to detect and prune orphan attachments (missing
  deployment parent) before blocking on live ones, with structured logging
- Update flow_version and deployment CRUD queries to join on Deployment so
  is_deployed status and attachment counts reflect only live attachments
- Document has_deployment_attachments write side-effect (orphan pruning)
- Wrap pre-sync orphan cleanup in try/except so failures don't abort the
  sync process
- Explicitly delete spans and traces in cascade_delete_flow to avoid FK
  violations from the span.trace_id constraint (which lacks ON DELETE
  CASCADE in the DDL)
- Add in-memory SQLite integration tests covering guard pruning, orphan
  cleanup, version pruning, deployment counts, and is_deployed status
- Add unit tests for sync exception-handling paths

* refactor(deployment): replace DB trigger guards with ORM-layer preflight checks
Remove the unshipped trigger migration (97c9a98c9c01) and enforce
deployment invariants at the ORM/service layer instead:
- Add orm_guards.py with preflight checks for all six guard codes
  (FLOW_DEPLOYED_IN_PROJECT, DEPLOYMENT_PROJECT_MOVE, DEPLOYMENT_TYPE_UPDATE,
  DEPLOYMENT_RESOURCE_KEY_UPDATE, DEPLOYMENT_PROVIDER_ACCOUNT_MOVE,
  DEPLOYMENT_PROVIDER_ACCOUNT_IDENTITY_UPDATE, CROSS_PROJECT_ATTACHMENT)
- Wire guards as pure prefix blocks into deployment, provider-account,
  and attachment CRUD functions — existing write logic unchanged
- Add preflight validation before bulk update(Flow) statements in
  projects.py and folder/utils.py — existing statements unchanged
- Add ensure_flow_move_allowed to flows_helpers.py for ORM-instance
  update paths (_patch_flow, _update_existing_flow, _validate_and_assign_folder)
- Optimize batch flow-move guard to one query per source folder
- Simplify parse_deployment_guard_error to isinstance-only chain walk
  now that guards are raised explicitly as DeploymentGuardError
- Document retry helper contract: operations must enforce guards internally
- Replace trigger-centric tests with ORM/service-level guard tests
- Add project API guard integration tests for flow-move blocking

* fix(deployment): prune attachment rows before deployment deletes
Explicitly delete flow-version deployment attachments before deployment row deletes
to prevent orphaned attachments when FK cascades are disabled (e.g. SQLite
foreign_keys=OFF). Keep delete-by-resource-key and delete-by-id behavior intact
while documenting guard None-scope behavior and adding coverage for missing-row
and FK-disabled cleanup paths.

* recover lost tests

* misc

* remove deployment guard error extraction from lfx session scope

* extract re-raise logic into a helper

* fix: stabilize deployment guard retries and guard diagnostics
- scope WxO provider-client ContextVar state to each deployment provider scope via provider_clients_memoization_scope
- compose WxO scope lifecycle into deployment_provider_scope and document forward multi-adapter dispatch options
- add successful provider resource-key sync debug logging and remove stray print-based sync error debug
- log DeploymentGuardError details during flow cascade delete and in deployment guard exception re-raise helper
- fix folder defaulting guard precheck query to use Flow.folder_id.is_(None) so NULL-folder flows are included
- deduplicate require_non_empty by importing from langflow.services.database.utils in deployment mappers
- remove deprecated duplicate helper module at api/v1/mappers/deployments/util.py and keep package export wired to canonical helper
- add regression test for default-folder guard precheck covering NULL-folder flow handling

* add tests for provider scope context

* fix: improve deployment guard logging and async guard handling
Log DeploymentGuardError directly in flow/project endpoint guard catch blocks with operation context, and use the async guard helper for generic exception paths that need guard parsing. Add tests for async guard helper logging and raise behavior.

* less verbose name for scope

* harden delete_unbound_attachments by passing provider account id to it to scope deletion

* fix: return dense deployment attachment counts and simplify snapshot sync
Make `count_attachments_by_deployment_ids` return a dense mapping for all
requested deployment ids, including zero for ids with no countable attachments.
Update deployment listing code to rely on direct key access for corrected counts,
remove redundant `verified_snapshot_ids` plumbing from snapshot sync call sites,
keep local snapshot id integrity checks in sync logic, and align backend tests
plus log messaging with the new count contract.

* guard context scope

* test: expand deployment guard and sync regression coverage
Add comprehensive regression coverage for deployment guard and deployment-sync behavior across database and API test suites.
- verify deployment deletes remove linked attachment rows
- cover batched move guards and immutable deployment/provider fields
- validate cascade flow deletion and user-scoped orphan cleanup behavior
- exercise retry helper edge cases for flow/project guard retries
- align sync helper tests with stricter provider-id/error handling and dedupe/early-return paths
- add API 409 guard-path tests for deployed flow/project delete and move scenarios

* fix: align flow-version list semantics with provider-scoped deployment status
- remove `deployment_ids` handling from `GET /flows/{flow_id}/versions/` and keep plain list mode as versions-only
- enforce provider-account ownership for `deployment_provider_id` via `get_owned_provider_account_or_404` (404 on unknown/foreign ids)
- add explicit feature-flag guard for provider-scoped mode:
  `Cannot use deployment_provider_id: the wxo_deployments feature flag is disabled`
- remove `has_providers` / `count_provider_accounts` branching and drive behavior directly from request params
- replace `get_flow_version_list` with `get_flow_versions_with_provider_status` and scope `is_deployed` by:
  `Deployment.deployment_provider_account_id == provider_account_id`
- keep best-effort `sync_flow_version_attachments` before provider-scoped reads
- update API tests:
  - delete deployment_ids endpoint tests
  - add provider-scoped status regression coverage (true under provider A, false under provider B)
  - add provider-id feature-flag rejection test (400)
  - add unknown and foreign provider-id ownership tests (404)
  - simplify feature-disabled plain-list test to assert `is_deployed` remains omitted
- migrate CRUD/in-memory tests off removed `get_flow_version_list` to `get_flow_versions_with_provider_status`
- update stale flow export TODO comments referencing the removed function

* fix: batch stale deployment cleanup per provider sync group
Replace per-deployment stale deletes in deployment sync with a single batched delete per provider group.
Add a bulk deployment-delete CRUD helper and regression coverage for grouped stale-delete behavior plus multi-deployment attachment cleanup.

* test: expand deployment guard and provider account update coverage
Add direct ORM guard coverage for noop immutable updates and attachment project matching. Update in-memory deployment tests to cover provider account API key rotation and align stale fixtures with current model constraints.

* fix: make deployment GET sync binding-aware and harden guard/prune behavior
- Align SQLite defaults in `lfx/services/settings/base.py`:
  - remove forced `foreign_keys=ON` pragma from default sqlite pragmas
- Replace deployment GET snapshot-id verification with binding-aware reconciliation in `api/v1/deployments.py`:
  - resolve and use the concrete deployment mapper in GET
  - call mapper-provided `extract_snapshot_bindings_for_get(...)`
  - prune detached links via `delete_unbound_attachments(...)` inside `session.begin_nested()`
  - fall back safely to unverified attachment counts when sync is unsupported or fails
  - change provider_data response shaping to `mapper.shape_deployment_get_data(...)` to hide internal fields
- Extend mapper contracts in `api/v1/mappers/deployments/base.py`:
  - add `extract_snapshot_bindings_for_get(...)` (explicitly raises `NotImplementedError` by default)
  - add `shape_deployment_get_data(...)` (explicitly raises `NotImplementedError` by default)
  - make GET sync/shaping behavior explicit per provider instead of accidental passthrough
- Implement wxO-specific GET sync/shaping in `watsonx_orchestrate/mapper.py`:
  - parse/validate `provider_data.tool_ids` for binding extraction
  - emit `ProviderSnapshotBinding(resource_key, snapshot_id)` for each tool id
  - validate/shape GET `provider_data` to expose only `{"llm": ...}` to API clients
  - raise clear 500/value errors for malformed adapter payload contracts
- Update wxO adapter GET payload in `watsonx_orchestrate/service.py`:
  - always include `tool_ids` and derived `environment`
  - include `llm` when available
  - remove `or None` fallback so mapper receives consistent structure
- Harden flow-version pruning in `services/database/models/flow_version/crud.py`:
  - resolve concrete version IDs to prune first
  - delete `FlowVersionDeploymentAttachment` children before deleting `FlowVersion` rows
  - prevent stale doubly-orphan attachment rows when SQLite runs with foreign keys disabled
- Simplify guard exception semantics in `deployment/exceptions.py`:
  - stop walking chained exceptions; only treat explicit `DeploymentGuardError` instances as guard failures
  - add optional remap hook to `araise_if_deployment_guard_error_or_skip(...)`
  - add `remap_flow_guard_for_project_delete(...)` to convert flow guard code to project guard code where needed
- Remove duplicated per-route guard catch blocks and centralize behavior:
  - `api/utils/flow_utils.py`, `api/v1/flows.py`, and `api/v1/projects.py` now rely on shared async guard helper
  - project delete path now remaps `FLOW_HAS_DEPLOYED_VERSIONS` into `PROJECT_HAS_DEPLOYMENTS` via shared remap function
- Keep multi-delete flow route behavior consistent in `api/v1/flows.py`:
  - run guard remap/log helper in broad exception path before generic logging and 500 handling
- Update deployment exception tests in `test_exceptions.py`:
  - remove chain-parsing expectations
  - validate direct guard-raise semantics
  - add remap function coverage
- Expand flow version pruning tests:
  - `test_crud.py`: verify delete ordering (attachments first, versions second) and no-op behavior when nothing is pruned
  - `test_in_memory.py`: add broad regression class proving orphan attachment cleanup, live deployment preservation, cross-flow/user isolation, and repeated-cycle convergence
- Expand deployment API/mapper/sync test coverage:
  - `test_deployment_mapper_base.py`: assert new base mapper GET shaper raises until implemented
  - `test_deployment_route_handlers.py`: migrate assertions to binding-aware sync behavior, unsupported-provider fallback, sync-failure fallback, and provider_data sanitization
  - `test_deployment_sync.py`: add wxO mapper tests for `extract_snapshot_bindings_for_get(...)` and GET data shaping requirements
  - `test_watsonx_orchestrate.py`: validate wxO GET provider_data now includes `tool_ids`/`environment` defaults and feature-flag-scoped provider context behavior

* remove redunant description

* remove agent environment from payload for get()

* fix(deployment): prune orphan attachments before reading in flow guard

The cascade flow delete path called `check_flow_has_deployed_versions`,
which issued a SELECT before any write. On SQLite that left the
connection holding only a SHARED lock; the subsequent cascade DELETEs
then had to upgrade SHARED -> RESERVED, which doesn't busy-wait when
another connection already holds RESERVED, surfacing intermittently
in CI as `OperationalError: database is locked`.

Reorder the guard to issue a single DELETE-with-scalar-subquery for
orphan attachments first, so the connection acquires the writer lock
up front. The live-attachment SELECT runs afterwards on the same
session. As a side benefit, orphan pruning now also runs when live
attachments coexist (previously the early raise short-circuited it,
though SAVEPOINT rollback in the retry wrapper still discards the
prune in that failure path).

Log the raw driver-reported `rowcount` verbatim on every prune for
debuggability, and update guard tests for the new DELETE-then-SELECT
ordering.

* add log that pruning might be rolled back

* fix(deployment): set provider scope when reconciling before provider-account delete
The DELETE /api/v1/deployments/providers/{id} handler called
list_deployments_synced without entering deployment_provider_scope, so the
WxO adapter raised CredentialResolutionError ("Deployment account context is
not available for adapter resolution"). Reconciliation was silently dropped
and the stale local count blocked every delete with a 409.
Wrap the reconciliation call in deployment_provider_scope(provider_account.id)
to match every other adapter call site in this module, so stale local
deployment rows can actually be pruned and the provider account can be
removed when the provider no longer owns any resources.
Add three regression tests in TestProviderAccountRoutes:
- assert deployment_provider_scope is active (with the right provider id)
  during reconciliation, and that the context does not leak afterwards
- assert that a CredentialResolutionError from reconciliation falls back to
  the local count and returns 409 without deleting the provider row
- assert that reconciliation is skipped entirely when no local deployments
  exist for the provider

---------

Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
(cherry picked from commit 60bb53b44c)
2026-04-23 17:49:52 -07:00
b8fe970493 fix(mcp): close path traversal + cross-user disclosure (PVR0754098) (#12818)
* fix(mcp): close path traversal + cross-user disclosure in MCP endpoint (PVR0754098)

Path-containment on the storage service was only enforced in save_file;
get_file/get_file_stream/delete_file/get_file_size all resolved user-supplied
names directly, letting an authenticated user read arbitrary files via the
MCP resources/read handler. resources/list and tools/list additionally
returned every user's flows regardless of ownership.

- storage/local.py: extract path-containment into shared _validated_path()
  and call it from every read/write/delete entry point (langflow + lfx).
- mcp_utils.handle_read_resource: reject filenames containing ../, /, \ at
  the handler layer; require a current user and verify flow ownership
  (or self-owned user bucket) before dispatching to storage.
- mcp_utils.handle_read_resource: accept optional project_id so project
  servers can't read resources outside the project's flows.
- mcp_projects: pass project_id into handle_read_resource.
- mcp_utils.handle_list_resources / handle_list_tools: scope flow queries
  to the authenticated user on the global server.

Regression tests cover traversal rejection on every read path, cross-user
flow access denial, project-scope enforcement, and unauthenticated list
queries returning empty.

* fix(mcp): drop user-bucket files from project-scoped resources/list

Project-scoped handle_list_resources still appended every UserFile owned
by the caller, so a project MCP client could enumerate user-level files
unrelated to the project. User files have no project association, so
skip them entirely when project_id is set.

Adds a regression test proving that project-scoped resources/list returns
only files from flows in the project.

(cherry picked from commit f0fd436fe9)
2026-04-23 17:49:52 -07:00
dc2411cb18 fix(custom): honor asname in from X import Y as Z for custom components (#12813)
* fix(custom): honor asname in `from X import Y as Z` for custom components

`_handle_module_attributes` was keying `exec_globals` on `alias.name`, so
`from pkg import Foo as Bar` bound `Foo` instead of `Bar` in a custom
component's exec scope. Any reference to `Bar` then raised NameError and
the component failed to load.

Use `alias.asname or alias.name`, matching the `import X as Y` branch in
the same file and standard Python import semantics. Adds regression tests
against `prepare_global_scope` and end-to-end through `create_class`.

* fix: Make sdk env flag idempotent (release-1.9.1) (#12815)

fix: Make sdk env flag idempotent

Refactor pytest_addoption to register CLI options via a loop with
contextlib.suppress(ValueError), allowing langflow-sdk and lfx to
coexist without plugin registration conflicts.

Add test_pytest_addoption_is_idempotent to verify behavior.

(cherry picked from commit cae2bdfef6)
2026-04-23 17:49:52 -07:00
c77c473bb2 feat: e2e tests for deployments api (#12767)
* update e2e tests

* fix(watsonx): harden existing-resource create flow and rollback journaling
- move the direct Watsonx adapter E2E runner to `scripts/e2e_deployment_tests/watsonx_orchestrate/adapter.py` (rename-only) so deployment E2E assets are consolidated under one folder
- add `scripts/e2e_deployment_tests/watsonx_orchestrate/api.py` with a full `/api/v1/deployments` matrix covering create/update happy paths, validation rejections, attachment patching, rollback/error paths, concurrency races, large payload tiers, and failpoint scenarios with owned-resource cleanup
- clarify existing-resource create behavior in `src/backend/base/langflow/api/v1/deployments.py`: DB-only onboarding keeps `created_*` fields empty unless provider mutation operations are requested
- add `util_create_result_from_existing_resource` in `src/backend/base/langflow/api/v1/mappers/deployments/watsonx_orchestrate/mapper.py` to normalize non-mutating onboard responses into a create-style result with empty `app_ids` and `tools_with_refs`
- extend `src/backend/base/langflow/services/adapters/deployment/watsonx_orchestrate/core/config.py` to accept `created_app_ids_journal` and append app ids immediately after successful provider connection creation for rollback safety
- update shared connection orchestration in `src/backend/base/langflow/services/adapters/deployment/watsonx_orchestrate/core/shared.py` to normalize provider app ids via `RawConnectionCreatePlan.__post_init__`, propagate `created_app_ids_journal`, dedupe rollback ids, and wrap validation-stage failures as `ConnectionCreateBatchError` with rollback metadata
- wire journaling into create/update rollback flows in `src/backend/base/langflow/services/adapters/deployment/watsonx_orchestrate/core/create.py` and `src/backend/base/langflow/services/adapters/deployment/watsonx_orchestrate/core/update.py` so partial provider-side connection creation is always captured for cleanup
- add mapper coverage in `src/backend/tests/unit/api/v1/test_deployment_mapper_watsonx.py` for existing-resource create-result normalization
- expand `src/backend/tests/unit/services/deployment/test_watsonx_orchestrate.py` with coverage for provider app-id normalization, validation-failure rollback metadata, and create/update rollback when failures occur after provider connection creation; update mocks/monkeypatch targets to match the shared connection entrypoint signature

* simplify create result logic (improves error friendliness

(cherry picked from commit 9f42c9e707)
2026-04-23 17:49:51 -07:00
0a34421a0e feat(google): refresh Gemini model list and enable tool calling for Gemini 3 (#12797)
* feat(google): refresh Gemini model list and enable tool calling for Gemini 3

Updates the Google Generative AI model metadata to reflect the current Gemini
API lineup and langchain-google-genai 4.1.3 capabilities:

- Enable tool_calling for all Gemini 3 preview models (supported by
  langchain-google-genai 4.1.3, which handles Gemini 3 thought signatures)
- Remove gemini-3-pro-preview (shut down March 9, 2026; replaced by
  gemini-3.1-pro-preview which is already listed)
- Add gemini-3.1-flash-lite-preview (Gemini 3.1 Flash Lite)
- Add gemini-3.1-flash-image-preview (Nano Banana 2)
- Drop the stale TODO now that Gemini 3 tool calling is wired up

* [autofix.ci] apply automated fixes

* fix(models): flatten list-shaped AIMessage content so Gemini 3 works

Gemini 3 models (via langchain-google-genai >=4.1.0) return AIMessage.content
as a list of content blocks, e.g.

    [{"type": "text", "text": "...", "thought_signature": "..."}]

Message(text=...) only accepts strings/iterators, so building the Language
Model component with gemini-3.1-pro-preview (or any other Gemini 3 model)
raised a pydantic ValidationError with three "text.*" errors.

Add _normalize_message_content() which concatenates text blocks and drops
non-text blocks, and run every AIMessage through it in _get_chat_result and
_handle_stream before constructing the Message.

Includes unit tests covering the Gemini 3 shape, multiple text blocks,
mixed non-text blocks, and defensive cases (None, empty list, missing text).

* fix(models): hide and clear api_key for providers without one (e.g. Ollama)

handle_model_input_update unconditionally forced api_key.show=True at the end
of the update cycle. For providers whose metadata doesn't map a variable to
api_key (Ollama is the only one today), this left a stale cross-provider
credential like OPENAI_API_KEY sitting behind a field that should not be
visible at all.

apply_provider_variable_config_to_build_config already sets show=True for
providers whose metadata DOES map api_key, so the force-show was redundant
for every other provider. Drop it, and when api_key is hidden by the provider
config step, also clear its value and load_from_db flag so switching back to
an api_key provider triggers the normal auto-populate path via the stale
cross-provider variable detection.

Updates the existing default-state test (which encoded the bug) and adds two
new regression tests: one for Ollama (hidden + cleared) and one for OpenAI
(visible stays visible).

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
(cherry picked from commit 737279943b)
2026-04-23 17:49:51 -07:00
974c5abc5e feat(deployments): filter WXO agent list to drafts when load_from_provider=true (#12745)
* feat(deployments): filter WXO list to drafts and expose environments as a list

When `load_from_provider=true` on `GET /deployments`, restrict watsonx
Orchestrate results to draft agents only and surface their environment
metadata as `environments: list[str]` on both the list and status
responses.

- Add `BaseDeploymentMapper.resolve_load_from_provider_deployment_list_params`
  as an extension point; override in the WXO mapper to always inject
  `{"environment": "draft"}` when listing from the provider.
- In the WXO adapter's `list`, pop `environment` from `provider_params`
  before building the WXO query (it is not a native WXO query param) and
  apply it as a client-side membership filter.
- Skip forwarding `deployment_type` from the endpoint on the
  `load_from_provider` path; WXO only exposes agent deployments today and
  other list logic does not rely on it.
- Replace the single `environment` string with `environments: list[str]`
  in list and status `provider_data`. This is a breaking change to the
  provider-backed shape, accepted because the feature is still behind a
  flag.
- Introduce `get_agent_environments` with fail-fast access (no silent
  fallbacks/normalization) so WXO contract breaks surface immediately,
  and drop the now-unused `derive_agent_environment` categorizer.

Tests updated to cover the new filter, the list shape, the status shape,
and the fail-fast semantics of the helper.

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
(cherry picked from commit ab70449427)
2026-04-23 17:49:51 -07:00
957ade8880 chore: upgrade wxo adk to 2.8.0 (#12707)
* chore: update wxo adk (2.8.0)

* make sure all sites use the helper

* revert tests

* remove thin helper and use adk directly

(cherry picked from commit b4f0870980)
2026-04-23 17:49:51 -07:00
b398f63a09 test: add missing test coverage for wxo deployments (#12689)
* test(deployments): add comprehensive test coverage for WXO deployment schemas, hooks, and components

Frontend (11 new/expanded test files, ~170 tests):
- use-connection-panel-state: tests for all handlers, ID sanitization, env var management
- step-review: tests for summary display, flows, connections, masking, edit mode
- global variable hooks: tests for useGetGlobalVariables auth guard and Zustand hydration
- use-patch-deployment: fix incorrect spec field assertion, test URL/body destructuring
- deployment-expanded-row, deployment-info-grid, deployment-flow-list: component tests
- flow-list-panel, version-panel, connection-search-list: panel component tests
- deployment-stepper-payload-builders: tests for buildConnectionPayloads,
  buildDeploymentUpdatePayload, buildDeploymentPayload, buildProviderAccountPayload

Backend (2 expanded test files, ~21 new tests):
- test_deployments_response_mapping: tests for update/list/execution/config response shapes
- test_deployment_schemas: tests for pagination constraints, execution schemas, snapshot schemas

* Fix nested button issue; remove more unnecessary tests

* update fe tests

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes

* fix tests; filter sql warnings

* ruff

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Viktor Avelino <64113566+viktoravelino@users.noreply.github.com>
(cherry picked from commit 3fb40f3465)
2026-04-23 17:49:51 -07:00
7b06c2033c fix(frontend): filter duplicate draft/live connections in deploy modal (#12724)
* fix(frontend): filter duplicate draft/live connections in deploy modal

Connections with same app_id can exist as both draft and live, causing
both to appear selected simultaneously. Filter to draft-only and add
environment badge for clarity.

LFOSS-3373

* fix: remove connection environment badge

(cherry picked from commit 899f82de5b)
2026-04-23 17:49:51 -07:00
5307f400bf feat: filter out live connections from wxo adapter (#12744)
* filter out live configs

* remove test, trust adk and wxo api

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
(cherry picked from commit ea57e9a57f)
2026-04-23 17:49:51 -07:00
6b0c95d192 refactor: Center truncate deployment provider URL (#12579)
* changed Deployment Provider URL to truncate in the center

* fix: center-truncate deployment provider URL using provider_data.url

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
(cherry picked from commit 4f9bd2bcf4)
2026-04-23 17:49:51 -07:00
89d1c60bed feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options (#12313)
* feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options

* docs: Add Gunicorn configuration details to environment variables documentation

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add unit test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
(cherry picked from commit 9d14d3b34e)
2026-04-23 17:49:51 -07:00
242c6c70b5 fix: MCP Tools component now works in LFX standalone mode (#12760)
* fix: MCP Tools component now works in LFX standalone mode

When serving a flow via `lfx` (the standalone flow server) without the
full Langflow package installed, `MCPToolsComponent.update_tool_list()`
unconditionally tried to import `langflow.api.v2.mcp.get_server` and
open a DB session, raising `ImportError` before the existing fallback
to `server_config_from_value` in `resolve_mcp_config()` could run.

Treat the ImportError as the expected signal of standalone mode: skip
the DB lookup and use the server config embedded in the flow JSON.
The DB path is unchanged when Langflow is available.

* [autofix.ci] apply automated fixes

* fix: narrow LFX standalone fallback to missing Langflow modules only

Previously the fallback to the flow-embedded MCP server config triggered
on any ImportError from the Langflow imports, which would silently swallow
transitive dependency failures (e.g. sqlmodel failing to import inside
langflow.services.database.models.user.crud). That is a real bug in the
full Langflow stack — not LFX standalone mode — and silently using the
stale flow-embedded config would hide it while also bypassing the DB
config that is supposed to take precedence when Langflow is available.

Narrow the fallback to ModuleNotFoundError whose .name is one of the
Langflow modules we are trying to import; re-raise everything else so it
surfaces normally.

* Update component_index.json

* [autofix.ci] apply automated fixes

* fix: address review feedback on LFX standalone fallback

- Expand comment on `except ModuleNotFoundError` narrowing to explicitly
  document the ImportError-vs-ModuleNotFoundError distinction.
- Bump fallback log from adebug to ainfo (mode switch is material).
- Simplify `getattr(e, "name", None) or ""` to `e.name or ""`.
- Add TODO(legacy-cleanup) marker flagging 800-line file debt.
- Rename fallback tests to `should_[expected]_when_[condition]` convention.
- Add regression tests: plain ImportError surfaces; lfx.services.deps
  missing surfaces (locks in prefix-check precedence).
- Document why `patch("builtins.__import__")` is necessary in tests.

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-23 21:42:42 +00:00
09b06415d1 fix: MCP server PATCH field clearing in backend and frontend [LE-891] (#12763)
* fix(backend): preserve MCP server fields on patch [LE-891]

* fix(frontend): clear last MCP header and env on update [LE-891]

* fix(frontend): invalidate MCP server patch query by name [LE-891]

* test(frontend): cover clearing last MCP header and env [LE-891]

* test(backend): cover MCP header replacement semantics [LE-891]
2026-04-23 20:22:26 +00:00
0217f704df fix: serialize concurrent MCP session access to prevent race conditions (#12761)
* fix: serialize concurrent MCP session access to prevent race conditions

Two MCPTools components pointing at the same SSE URL share a single
MCPSessionManager via the component cache. Under concurrent flow
execution (10+ runs against the same server), ~40-50% of runs failed
with one of:

  Error updating tool list: 'streamable_http_<hash>_0'
  Timeout updating tool list: ...
  Error updating tool list: <connection error>

Three races caused this:

1. get_session() was not serialized per-server. Concurrent callers
   iterating self.sessions_by_server[server_key]["sessions"] could both
   pass the health check, then both invoke _cleanup_session_by_id().
2. _cleanup_session_by_id() used del sessions[session_id] in a finally
   block. Two callers that both passed the `if session_id not in
   sessions` guard would race on the delete — the loser raised
   KeyError: 'streamable_http_<hash>_0', matching the reported symptom.
3. Session ids were generated from len(sessions), so removing and
   re-adding sessions could silently produce colliding ids.

Fixes:
- Per-server asyncio.Lock (guarded by a module-level lock for creation)
  serializes session reuse/creation/cleanup.
- _cleanup_session_by_id() now pops the session entry up front; only
  the winning caller runs teardown, the rest no-op.
- Session ids come from a monotonic per-server counter.

Regression tests cover all three races: 10 concurrent get_session
calls must share a single created session; 10 concurrent cleanups
must not raise; session ids must not recycle "_0" after cleanup.

Fixes langflow-ai/langflow#9860

* fix: extend per-server lock to cleanup paths and reclaim per-key maps

Addresses review findings on the previous commit:

HIGH: cleanup paths bypassed the per-server lock. Both
`_cleanup_session(context_id)` (invoked from client disconnect) and
`_cleanup_idle_sessions()` (invoked from the periodic background task)
still mutated `sessions_by_server` without holding the lock. A
concurrent `get_session()` could finish validating a session while
idle-cleanup popped and cancelled its task; `get_session()` then
returned a dead session and registered a dangling refcount entry.

MEDIUM: per-server maps (`_server_locks`, `_session_id_counters`)
grew unboundedly. The previous patch only reclaimed server entries
from `sessions_by_server`; rotating auth/session headers (which
change `server_key` via `_get_server_key`) leaked entries in the
other two maps forever in long-lived processes.

Changes:
- Replace `_get_server_lock()` with `_server_lock()`, an async
  context manager that pin-counts the entry so reclamation can't
  race a task that holds or is about to acquire the lock.
- Wrap the mutating regions of `_cleanup_session()` and
  `_cleanup_idle_sessions()` in `_server_lock(server_key)`.
- On lock release, reclaim both `_server_locks[server_key]` and
  `_session_id_counters[server_key]` once pins drop to 0, the lock
  is unheld, and the server has no remaining sessions.
- `cleanup_all()` also clears the two maps under `_locks_guard`.

New regression tests:
- `test_cleanup_idle_vs_get_session_are_serialized` — an in-flight
  `get_session` blocks a concurrent idle-cleanup pass; the session is
  returned live, not torn down underneath the caller.
- `test_concurrent_cleanup_session_and_get_session_safe` — refcount
  transitions stay consistent across concurrent connect/disconnect.
- `test_server_lock_and_counter_reclaimed_when_unused` — after all
  sessions for a `server_key` are removed, both `_server_locks` and
  `_session_id_counters` entries go away.

* fix: CAS context mapping pop to preserve cross-server handoffs

Addresses the review finding on cross-server reuse of `context_id`:

`_cleanup_session(context_id)` runs under the per-server lock of the
server the mapping pointed at when cleanup started. That lock does
NOT serialize a concurrent `get_session(context_id, different_server)`
which runs under a *different* per-server lock and atomically
re-points `_context_to_session[context_id]` at the new session.

The old cleanup path then ran
`self._context_to_session.pop(context_id, None)` unconditionally,
wiping out the fresh mapping. The new session on the other server
was left with refcount 1 and no context mapping, so the next
disconnect was a no-op and the session leaked indefinitely.

Fix: CAS the pop. After the refcount decrement / teardown, only drop
the mapping if `_context_to_session[context_id]` still points at
`(server_key, session_id)` — the pair we just cleaned up. The
`get()` and `pop()` run synchronously (no `await` between them) so
asyncio cannot interleave another coroutine between the check and
the mutation.

New regression test `test_cleanup_does_not_wipe_cross_server_handoff`
slows down `_cleanup_session_by_id` for server A while a concurrent
`get_session(ctx, serverB)` re-points the context at server B.
Without the CAS, the assertion that `_context_to_session[ctx] ==
(server_B, ...)` fails. With the CAS, the fresh B mapping survives
and its refcount is intact.

* refactor(mcp): address review findings on concurrent-access fix

IMPORTANT
- Replace `dict[str, Any]` with `_ServerLockEntry` TypedDict so the lock/pins
  shape is visible to static analysis (finding #1).
- Add TODO marker above `MCPSessionManager` flagging the module's file-size
  debt as follow-up work (finding #2).

RECOMMENDED
- Replace timing-based `asyncio.sleep(0.02)` / `asyncio.sleep(0.05)` in
  concurrency regression tests with deterministic Event-based rendezvous
  and pin-count polling (finding #3).
- Introduce `_sessions_for(server_key)` helper and use it in
  `_cleanup_idle_sessions`, `_cleanup_session_by_id`, and `cleanup_all` to
  stop reaching through `sessions_by_server[server_key]["sessions"]` at
  every call site (finding #4).
- Document why `_cleanup_session_by_id` keeps a broad `except Exception`
  (transport-layer teardown raises many different exception hierarchies —
  leak-on-cleanup is worse than a swallowed error) (finding #5).
- Log a warning when pin count goes negative in
  `_release_server_lock_if_idle` so a missing acquire / double release
  surfaces in telemetry instead of being silently swept (finding #6).
- Document the "caller must hold `_server_lock(server_key)`" invariant on
  `_next_session_id` (finding #7).

NICE TO HAVE
- Annotate the `_server_lock` async context manager yield type as
  `AsyncIterator[None]` for IDE support (finding #8).

All 175 tests in `test_mcp_util.py` still pass; ruff clean.
2026-04-23 18:25:12 +00:00
ac654b2e71 fix(frontend): preserve IME composition for dead-key accents (#12801)
* fix(frontend): preserve IME composition for dead-key accents in inputs

Dead-key accents (macOS Option+e, Linux dead keys, CJK IME) were dropped in
node inputs because the store update cascade (100-200ms per keystroke) plus
a `setSelectionRange` call on every `value` change aborted browser IME
composition. Typing `Option+e a` landed as `´a` instead of `á`.

Introduce a shared `useIMEInput` hook that keeps a local `displayValue`
mirror so React's controlled value always matches the DOM, defers `onCommit`
until `compositionend`, NFC-normalizes the composed string, and guards the
cursor-restore effect while composing.

Wire the hook into `InputComponent` (form branch extracted to
`FormInputBranch` so the hook is only mounted when active),
`CustomInputPopover`, `CustomInputPopoverObject` (gated on text-entry mode
so selection-mode renders stay read-only), `CursorInput`, and
`TextAreaComponent`.

* test(frontend): add adversarial IME tests and tighten popoverObject guards

Address code-review feedback on the dead-key accent fix:

- Extract `useIMEInputForOnChange` helper so popover, popoverObject, and
  cursor-input stop duplicating cursor state + commitValue wiring.
- Export `normalizeNFC` from the hook and reuse it in `TextAreaComponent`'s
  modal setValue instead of duplicating the branch inline.
- Null-guard `options?.find`, `(selectedOptions ?? []).map`, and the
  CommandList `options ?? []` so `CustomInputPopoverObject` no longer
  crashes when selection-mode flags toggle before data props populate.
- Coerce `readOnly` to a boolean to avoid passing a function reference.
- Add adversarial tests that cover:
  - null/undefined `value` prop (hook fallback).
  - orphan `compositionstart` without `compositionend`.
  - multiple `compositionstart` before a single `compositionend`.
  - null `selectionStart` on `compositionend`.
  - absent `String.prototype.normalize` fallback.
  - parent rerenders mid-composition — `onCommit` stays suppressed.
  - `FormInputBranch` folder-rename synth event (idle + IME paths).
  - `CursorInput` ref forwarding for both function and object refs.
  - `CustomInputPopoverObject` selection-mode gating + null-guard paths.

Coverage on the fix surface: `use-ime-input.ts` 100% stmts / 93.1% branch,
`popoverObject/index.tsx` 86.6% stmts / 89.4% branch, `cursor-input.tsx`
100% stmts / 73.3% branch.

* [autofix.ci] apply automated fixes

* fix(frontend): flush IME composition on blur and unmount

Address review feedback on the dead-key accent fix (#9096):

- Add `flushPendingComposition` to `useIMEInput` so blur-mid-composition
  (macOS dead keys, Safari/WebKit) commits the composed buffer instead
  of dropping it and leaving the field stuck in composing mode. Wire
  into all 5 call sites: `InputComponent`, `CustomInputPopover`,
  `CustomInputPopoverObject`, `CursorInput`, `TextAreaComponent`.

- Add unmount-cleanup `useEffect` that commits the latest composition
  snapshot when the input unmounts mid-composition (popover close,
  modal dismiss). Snapshots tracked via `lastCompositionValueRef`;
  callback captured via `onCommitRef` to survive empty-deps cleanup.

- Add `cancelComposition` for non-text-mode swaps. `CustomInputPopoverObject`
  invokes it via `useEffect` when `isSelectionMode` toggles true, so
  the readOnly-render path can't strand the composing flag and silently
  swallow later plain keystrokes.

- Snapshot `compositionStartValueRef` so flush skips phantom commits
  when an orphan dead-key blur leaves `element.value` unchanged.

- Stabilize `commitValue` in `useIMEInputForOnChange` via `onChangeRef`
  so `flushPendingComposition` and `cancelComposition` keep stable
  identities across parent rerenders, preventing downstream
  `useEffect([cancelComposition])` from re-firing on every render.

- Move explicit `onBlur` after `{...imeInputProps}` in 4 components so
  future hook additions can't silently override the flush handler via
  spread order.

Tests: 7 new cases covering blur-flush NFC, stuck-flag reset,
unmount-mid-composition commit, no-op when not composing, no-op when
composition made no progress, `cancelComposition` clearing the flag,
stable callback identity across rerenders, latest-onChange via ref.
193/193 pass in `parameterRenderComponent`.

* test(frontend): add IME input regression

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Viktor Avelino <64113566+viktoravelino@users.noreply.github.com>
Co-authored-by: Viktor Avelino <viktor.avelino@gmail.com>
2026-04-23 14:52:13 +00:00
b48572dfa1 fix(security): drop URL substring checks for WXO auth scheme selection (#201-204) (#12842)
fix(security): drop URL substring checks for WXO auth scheme selection

Closes CodeQL alerts #201-204 (py/incomplete-url-substring-sanitization,
high). ``get_authenticator`` chose between the IBM Cloud (IAM) and MCSP
authenticators by testing ``".cloud.ibm.com" in instance_url`` /
``".ibm.com" in instance_url`` against the raw URL string. Because the
check scanned the whole URL, a provider URL whose hostname passes the
existing ``.ibm.com`` allowlist (e.g. ``https://api.sso.ibm.com``) but
whose path, userinfo, query, or fragment contains ``.cloud.ibm.com``
could flip the wrong authenticator:

    https://api.sso.ibm.com/.cloud.ibm.com       → wrongly IAM
    https://cloud.ibm.com@attacker.example/…    → wrongly IAM
    https://host.ibm.com/?trick=.cloud.ibm.com   → wrongly IAM

Scheme selection now drives off ``urlparse(instance_url).hostname``
with ``hostname == "…"`` or ``hostname.endswith(".…")`` — the exact
pattern documented as safe in CodeQL's rule help.

Also tightens the two test assertions flagged by the same rule
(``"cloud.ibm.com" in result.base_url``) to exact URL equality against
the normalised mapper output, and adds a parametrised regression test
covering the five known substring bypass shapes (path / query /
userinfo / fragment smuggling).

No behavioural change for real IBM Cloud / MCSP URLs: all 14
``get_authenticator`` tests and 403 tests across the two touched test
modules pass.
2026-04-22 21:13:13 +00:00
d3b60fa898 fix(security): use CodeQL-recognized sanitizer for path-injection alerts #205-213 (#12841)
* fix(security): use CodeQL-recognised sanitiser for path-injection sinks

Addresses CodeQL alerts #205-213 (py/path-injection, high). All three
modules already contained path-containment logic, but relied on
``pathlib.Path.resolve()`` + ``Path.is_relative_to`` / ``startswith``,
which CodeQL's dataflow model does not recognise as a sanitiser — so
the alerts kept firing on ``.exists()``/``.resolve()`` sinks.

Rewrites the sanitisers using ``os.path.realpath`` + ``startswith`` —
the pattern documented by CodeQL as the recognised traversal
sanitiser — without changing the security guarantees. ``realpath``
canonicalises and follows symlinks, so the containment check is
robust against both ``..`` sequences and symlink escapes.

Files touched (one sanitiser per file):

* ``agentic/services/helpers/flow_loader.py`` — replace
  ``_validate_path_within_base`` with ``_safe_resolved_path`` that
  returns the canonicalised path, then have ``resolve_flow_path`` use
  that canonicalised path for all downstream ``.exists()`` calls.
* ``api/v1/files.py`` — rewrite the profile-picture lookup to derive
  the candidate path from ``realpath`` of base + user components and
  verify containment before any filesystem access.
* ``api/v1/flows_helpers.py`` — collapse the absolute/relative
  branches of ``_get_safe_flow_path`` into a single
  ``realpath`` + ``startswith`` containment check. Drops the now-
  unused ``pathlib.Path as StdlibPath`` import.

No behavioural changes: all 92 existing path-validation/flow-loader
unit tests and 54 ``files.py`` tests still pass.

* fix(security): don't leak resolved base path in flow path error detail

The containment-failure branch for absolute paths returned the fully
resolved flows base directory (e.g. /var/lib/langflow/flows/<user-uuid>)
in the HTTP 400 detail, exposing server filesystem layout and the user's
internal path component. Replace with a static message — the specific
resolved path adds no diagnostic value for the client and leaks internal
detail.
2026-04-22 21:12:40 +00:00
c2bef76456 fix: update npm overrides (#12843)
chore(frontend): update npm overrides

Co-authored-by: Janardan S Kavia <janardanskavia@Janardans-MacBook-Pro.local>
2026-04-22 21:00:37 +00:00
dc5b27e8ca fix(frontend): keep KnowledgeBase modal centered when side panel opens (#12764) 2026-04-22 15:56:44 -03:00
3ea28ed902 fix: propagate x-api-key and authorization headers to nested MCP calls (#12541)
* fix: propagate x-api-key and authorization headers to nested MCP calls (fixes #12529)

When Langflow is used as an MCP server containing flows with nested MCP
components, authentication headers like x-api-key were silently dropped
because extract_global_variables_from_headers() only captured headers
with the X-LANGFLOW-GLOBAL-VAR-* prefix.

Add _AUTH_HEADERS_TO_PROPAGATE to also capture x-api-key and authorization
under their lowercase header names. These values are stored in the
request_variables context, making them available for resolution in nested
MCP server configs. Users can now reference them in their server headers
config as {x-api-key: x-api-key} to propagate the incoming key.

* fix: remove duplicate verify_public_flow_and_get_user from core.py

The duplicate function in core.py referenced undefined names (uuid, session_scope)
and shadowed the canonical implementation in flow_utils.py, which is the one
re-exported from __init__.py and has the fuller signature including
authenticated_user_id. Removing the dead copy resolves the F821 ruff errors.

* Tighten scope of fix

* Update .secrets.baseline

* Clean up test locations

* Update .secrets.baseline

---------

Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-21 23:53:43 +00:00
5d6a7a0f31 chore: address eric's comment 1
address eric's comment 1
2026-04-21 16:18:16 -04:00
88d95758b0 chore: add whitesource to ignore and scan all src
add whitesource to .gitignore and scan all of src
2026-04-21 16:18:16 -04:00
ce6507bee1 ci: add mend integration
add mend integration to OSS
2026-04-21 16:18:16 -04:00
3bc012440f fix: mcp component dyanamic tool (#12779)
* fix(mcp): support real-time tool onboarding via per-request auth headers

Problem
-------
When Langflow runs an agent that has an MCPTools component attached and an
API request supplies authentication headers via tweaks, the MCP server can
legitimately return an *expanded* tool list that is broader than the
unauthenticated base set (the server filters its listing by the caller's
identity). Today those extra tools never reach the agent at runtime: the
component always binds the first preloaded tool list and ignores the new
auth context for the lifetime of the flow.

Root cause
----------
Several caching / binding layers combine to prevent per-request tool
refresh:

1. Flow JSON persists ``component_as_tool`` with ``cache: true`` and
   Langflow's ``Output`` memoization reuses the first ``to_toolkit()``
   result for all subsequent requests regardless of headers.
2. The shared MCP server cache was keyed by server name only, so
   requests with different auth contexts collided on the same cache slot.
3. In tool-mode, ``_get_tools()`` returned ``[]`` when
   ``_not_load_actions`` was true, starving the agent of MCP tools.
4. Concurrent ``update_tool_list`` calls raced on the same Streamable
   HTTP client session, producing intermittent HTTP 404 /
   "Session terminated" errors from the MCP SDK.

What changed
------------
``src/lfx/src/lfx/components/models_and_agents/mcp_component.py``:

- FIX 1 - Output cache bypass: ``_build_tool_output`` declares the
  Toolset output with ``cache=False``, and ``map_outputs`` overrides any
  ``cache: true`` value persisted in saved flow JSON. Together these
  guarantee every run resolves a fresh ``to_toolkit()`` call regardless
  of whether the flow was loaded from disk. Independent of the
  user-facing ``use_cache`` / "Use Cached Server" toggle.

- FIX 2 - Header-aware cache key: ``_mcp_servers_cache_key`` now hashes
  the component headers into the shared server-cache key so requests
  with different auth contexts land in distinct cache slots instead of
  masking each other. The shared cache is bounded at
  ``SHARED_SERVERS_CACHE_MAX_ENTRIES`` = 64 with FIFO eviction so a
  tenant rotating session tokens does not grow the map without limit.

- FIX 3 - ``_get_tools`` always fetches: removed the ``_not_load_actions``
  short-circuit that returned ``[]`` in tool-mode, so the agent always
  binds the current (possibly expanded) tool list. ``_not_load_actions``
  still gates only the UI build_config dropdown.

- FIX 4 - Serialized ``update_tool_list``: an ``asyncio.Lock`` prevents
  concurrent refreshes from racing on the same Streamable HTTP client
  session, eliminating the intermittent HTTP 404 / "Session terminated"
  errors the MCP SDK raised when session DELETE and POST overlapped.

- FIX 5 - TTL tool cache: ``_get_tools`` keeps a per-instance,
  header-hash-keyed TTL cache (``TOOL_TTL_SECS`` = 30s, disable with 0)
  bounded at ``TOOL_TTL_MAX_ENTRIES`` = 32 with FIFO eviction and
  stale-on-read drop. Parallel agent steps that share the same auth
  reuse the tool list instead of each paying for a fresh MCP round-trip.
  This is deliberately distinct from ``use_cache`` / "Use Cached Server"
  which controls the cross-request shared cache. The dict is initialized
  in ``__init__`` so every instance gets its own cache.

Impact on users
---------------
- Backward compatible. Unauthenticated / non-tweaked flows behave
  identically: the TTL cache is per-instance and scoped to a single
  (server, header-hash) pair, and the base-class ``to_toolkit`` chain is
  unchanged (no override of filtering via ``tools_metadata`` /
  ``enabled_tools``).
- New behaviour only triggers when tweaks/UI headers include an auth
  context; the header-hash cache key then isolates per-caller tool lists
  and the agent binds the correctly-filtered expanded set.
- No changes to ``lfx/services/settings/base.py``: the global
  ``mcp_server_timeout`` default stays at its existing value.

Test plan
---------
- ``python -m py_compile`` passes on the edited module.
- ``python -m ruff check`` reports no new violations introduced by this
  change.
- Local verification against an MCP server that returns different tool
  lists per caller identity:
  * Unauthenticated request returns the base tools as before.
  * Authenticated request (auth headers via tweaks) returns the
    expanded tool set on the first call and on subsequent calls with
    the same auth - TTL cache hits log
    ``MCP _get_tools: TTL cache hit``.
  * Switching to a different auth context on the same flow returns the
    correct per-caller tool list (header-hash cache key isolates the
    two contexts).
  * Parallel agent runs no longer surface HTTP 404 / "Session
    terminated" from the MCP SDK.
  * Disabling a tool via the UI (``tools_metadata``) correctly hides it
    from the bound agent - base-class filtering is preserved.

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes (attempt 3/3)

* test(mcp): cover dynamic-tool onboarding fixes

Add unit coverage for the fixes introduced in this PR so regressions in the
cache-key, TTL cache, concurrency lock, shared-cache eviction, and Toolset
Output.cache=False behaviour are caught by CI instead of by users:

- Header normalization across list / dict / None / malformed shapes
- `_mcp_servers_cache_key` determinism + header-hash scoping across auth
  contexts (different headers → different keys; order-independent)
- Per-instance `_ttl_tool_cache` isolation (no cross-instance leakage)
- `_get_tools` TTL cache: hit skips `update_tool_list`, expired entries are
  refetched, FIFO-eviction caps growth at `TOOL_TTL_MAX_ENTRIES`, and TTL=0
  disables the cache
- `_update_tool_list_lock` serialises concurrent calls (peak concurrency 1)
- Shared `servers` cross-request cache evicts oldest entry when the map
  reaches `SHARED_SERVERS_CACHE_MAX_ENTRIES`
- `_build_tool_output` declares `cache=False`; `map_outputs` overrides
  persisted `cache=True` from saved flow JSON

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Mansura <mansura.nw@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-21 14:26:21 +00:00
8db793435e fix(frontend): show backend error detail on project upload failure (#12791)
* fix(frontend): read backend error detail on project upload failure

Backend returns error payload under `detail` (FastAPI convention), but
the upload handler was reading `message`, producing an empty alert list.
Switch to `detail` with `message` and raw-error fallbacks, typed via
AxiosError.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): simplify project upload error handler

Revert defensive AxiosError typing; keep direct `detail` read which is
sufficient for the upload error path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): guard upload error handler against missing response

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:05:43 +00:00
16432b61f2 fix(mcp): reconnect Streamable HTTP after MCP server restart (#12777)
…without bogus SSE fallback

When the remote MCP process restarts, Langflow kept a stale ClientSession:
tool list updates could fail with "Session terminated", the session
health check could re-raise instead of discarding the session, and
any Streamable HTTP failure triggered SSE fallback and could lock
transport preference to SSE—masking the real issue and producing
misleading TaskGroup/dual-transport errors.

Invalidate all sessions for the server URL on list_tools failure,
treat any health-check failure as a dead session, retry transient
Streamable HTTP errors before considering SSE, only fall back to
SSE for clear transport-mismatch signals (e.g. 404/405/406),
reset and re-record transport preference accordingly, and bust
sessions on run_tool using the same termination/connection signals.

Add unit tests with mocked streamablehttp_client and sse_client.
2026-04-20 13:48:31 +00:00
737d2c7a61 fix(frontend): cap MCP server modal height (#12743)
Modal grew past viewport as args/env rows were added, hiding fields
below (including Add Server button) with no scroll.

- add max-h-[75vh] on modal so it stops expanding
- add overflow-y-auto on tab body so inner content scrolls; header,
  tabs, and footer stay fixed
- add shrink-0 on footer so action buttons can't be squashed

Fixes LE-881
2026-04-17 19:22:45 +00:00
919724523c fix: resolve ghosting issues in preload optimization (#12587)
* fix: enable preload_app option in LangflowApplication configuration

* feat: Integrate Sentry and Prometheus support in worker lifespan

- Initialize Sentry SDK in the worker lifespan if a DSN is provided, allowing for better error tracking.
- Start Prometheus HTTP server if enabled in settings, enhancing monitoring capabilities.
- Added logging for both Sentry initialization and Prometheus server startup to aid in debugging and monitoring.

Refactor existing code to defer Sentry initialization to avoid issues with process forking. This change improves the overall observability of the application.

* fix: Correct Prometheus port validation logic in create_app function

- Updated the condition for validating the Prometheus port to use 'and' instead of 'or', ensuring that the port is both greater than 0 and less than MAX_PORT. This change enhances the reliability of the Prometheus server configuration.

* test: Add unit tests for LangflowApplication.pre_fork method

- Introduced a new test suite for the pre_fork method in LangflowApplication, covering various scenarios including warnings for non-main threads and non-LISTEN TCP connections.
- Implemented tests to ensure proper handling of psutil import errors and unexpected exceptions.
- Verified that garbage collection is always executed during the pre_fork process.
- Added a fake server mock to facilitate testing without requiring a real server instance.

* refactor: Move telemetry service initialization to lifespan context

- Removed the initialization of the telemetry service from the beginning of the `get_lifespan` function and added it within the lifespan context. This change ensures that the telemetry service is only initialized when needed, improving resource management and application performance.

* fix: Enhance Sentry integration with error handling and import checks

- Added error handling for Sentry SDK initialization to log warnings if the SDK is not installed or if initialization fails.
- Updated the `setup_sentry` function to conditionally import `SentryAsgiMiddleware`, logging a warning if the import fails.
- This improves the robustness of the application by preventing crashes related to missing Sentry dependencies and providing clearer logging for debugging.

* fix: Improve Prometheus server error handling in lifespan context

- Enhanced error handling for starting the Prometheus server by adding specific checks for ImportError and OSError.
- Added logging for cases where the Prometheus client is not installed or when the port is already in use, improving clarity for debugging and operational monitoring.
- This change aims to provide more informative feedback during server startup, enhancing the overall robustness of the application.

* refactor: Rename and enhance Sentry middleware integration

- Changed the function name from `setup_sentry` to `add_sentry_middleware` for clarity.
- Updated the middleware setup to ensure Sentry is attached at request time, deferring SDK initialization to the worker lifespan to avoid ghost transactions.
- Adjusted unit tests to mock the new middleware function name, ensuring continued test coverage and functionality.

* feat: Enhance pre_fork method to identify benign threads before forking

- Introduced a new class-level constant `_BENIGN_THREAD_PREFIXES` to define known benign thread prefixes.
- Added a class method `_is_benign_thread` to check if a thread is benign based on its name.
- Updated the `pre_fork` method to log warnings for non-benign threads, improving thread management during the forking process.
- Added a unit test to ensure no warnings are emitted for benign threads, enhancing test coverage for the `pre_fork` method.

* fix: Improve logging for psutil import error handling

- Added a debug log statement to indicate when the psutil library is not installed, enhancing visibility into the application's behavior during TCP connection checks.
- This change aims to provide clearer feedback for debugging and operational monitoring when the psutil dependency is missing.

* fix: Enhance garbage collection handling in pre_fork method

- Wrapped the gc.collect() call in a try-except block to prevent crashes if an exception is raised during garbage collection.
- Added logging to capture any warnings when gc.collect() fails, improving error visibility during the pre-fork process.
- Introduced a new unit test to ensure that the pre_fork method handles gc.collect() exceptions gracefully while still calling gc.freeze().

* docs: LANGFLOW_GUNICORN_PRELOAD environment variable introduced

* refactor: Update application setup for Windows and non-Windows environments

- Introduced conditional logic to handle application setup differently based on the operating system.
- Added a factory pattern for creating the FastAPI application, improving flexibility for non-Windows systems.
- Enhanced error handling to provide clear runtime messages when the application cannot be initialized correctly.

* refactor: move test_server.py to correct unit/base location

The test file exercises langflow.server which belongs to the base package
(src/backend/base/). Moving it to src/backend/tests/unit/base/ follows the
project convention and aligns with the path expected by CI ruff checks.

Made-with: Cursor

* feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options (#12313)

* feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options

* docs: Add Gunicorn configuration details to environment variables documentation

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add unit test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
2026-04-17 16:53:26 +00:00
351e2589c2 feat: allow users to name flow snapshots when saving (LE-874) (#12742)
Wire up the existing backend `description` field so users can optionally
name snapshots. The name appears in the version sidebar, deployment
modal version list, and canvas preview badge.

- Add SaveVersionDialog modal for entering version name on save
- Extract shared VersionLabel component for consistent display
- Thread previewDescription through versionPreviewStore to overlay badge
2026-04-17 14:35:08 +00:00
f748738c74 fix: MCP Auth Error on restart / swapping auth (#12715)
* fix: MCP Auth Error on restart / swapping auth

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* Make this more robust

* fix: Propagate the api key in PATCH

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-17 04:07:24 +00:00
a484c5676d fix(tests): Skip assistant-panel e2e test when OPENAI_API_KEY is missing (#12747)
add openai validation before start test

Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-16 22:35:35 +00:00
abc6bada8d fix: filter out null objects in mergeNodeTemplates function (#12688)
* fix: filter out null objects in mergeNodeTemplates function

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
2026-04-16 20:25:47 +00:00
712c724979 fix: respect renamed/deleted default folder across logins (#12746)
get_or_create_default_folder only matched the default folder by its
literal name (DEFAULT_FOLDER_NAME, e.g. "Starter Project"), so if a
user renamed the folder from the UI, the next login/server restart
would recreate a new "Starter Project" alongside the renamed one.

Now, if the user already has at least one folder, return one of their
existing folders instead of forcing a new default back into the UI.
Only create the default folder on true first-time setup (when the user
has zero folders). Legacy folder migration behavior is preserved.

Adds regression tests covering the rename case and the case where the
user keeps a non-default folder instead of "Starter Project".
2026-04-16 19:59:36 +00:00
5991ce1b9a fix: Make sdk env flag idempotent (#12716)
* fix: Make sdk env flag idempotent

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-15 21:36:22 +00:00
9d84ec14d9 chore: version bump 1.10.0
version bump
langflow: 1.10.0
langflow-base: 0.10.0
lfx: 0.5.0
langflow-sdk: 0.2.0
2026-04-15 16:11:11 -04:00
9aae6de814 ci: increase backend test timeout 2026-04-15 08:41:09 -07:00
da516b7045 Merge release branch 2026-04-14 16:44:45 -07:00