Skip to content

Flow deletion fails with ForeignKeyViolation when traces exist — span.trace_id FK missing ON DELETE CASCADE #12346

@renjithkmathew

Description

@renjithkmathew

Bug Description

Deleting a flow raises a psycopg.errors.ForeignKeyViolation IntegrityError when the flow has associated
execution traces (records in the trace and span tables). The deletion fails inside cascade_delete_flow
and surfaces as a 500 error in the ASGI application.

Error log:

Unable to cascade delete flow: 21e3cc42-cada-492b-8183-5fca183f65cd
IntegrityError: (psycopg.errors.ForeignKeyViolation) update or delete on table "trace"
violates foreign key constraint "span_trace_id_fkey" on table "span"
DETAIL: Key (id)=(e306f1a8-94d5-47a6-8206-579ed2c82203) is still referenced from table "span".

There are two compounding root causes:

Root Cause 1 — Missing ON DELETE CASCADE on span.trace_id FK at the database level

The migration 3478f0bd6ccb_add_trace_and_span_tables.py creates the span table's FK to trace
without ondelete="CASCADE":

# span → trace: MISSING ondelete="CASCADE"
sa.ForeignKeyConstraint(["trace_id"], ["trace.id"]),

# trace → flow: correctly has CASCADE
sa.ForeignKeyConstraint(["flow_id"], ["flow.id"], ondelete="CASCADE"),

When PostgreSQL cascades the flow → trace delete (which works), it attempts to delete trace rows next,
but fails because span records still reference them and the FK has no cascade rule.

Root Cause 2 — cascade_delete_flow never explicitly deletes trace or span records

cascade_delete_flow in src/backend/base/langflow/api/utils/core.py uses raw SQL delete() statements
and relies entirely on the database-level FK cascade from flow → trace → span. However:

  • Raw SQL bypasses SQLAlchemy ORM cascade (cascade="all, delete-orphan" defined on
    TraceTable.spans), so the ORM safety net never fires.
  • The function explicitly deletes MessageTable, TransactionTable, VertexBuildTable, and
    FlowVersion before deleting Flow, but never handles SpanTable or TraceTable,
    leaving them entirely dependent on the broken DB-level cascade chain.

Proposed Fix Options

Option Description
A (Application-level) Add explicit delete(SpanTable) and delete(TraceTable) statements to cascade_delete_flow before deleting Flow, matching the existing pattern for other child tables.
B (Database-level) Add a new Alembic migration to alter span.trace_id FK to include ON DELETE CASCADE, completing the cascade chain at the DB level.

Option A is immediately safe for both SQLite and PostgreSQL and requires no schema migration.
Option B is the more robust long-term solution and can be applied alongside Option A for defence in depth.


Affected Files

File Issue
src/backend/base/langflow/api/utils/core.py cascade_delete_flow missing explicit deletes for SpanTable and TraceTable
src/backend/base/langflow/alembic/versions/3478f0bd6ccb_add_trace_and_span_tables.py span.trace_id FK created without ondelete="CASCADE"
src/backend/base/langflow/services/database/models/traces/model.py ORM-level cascade defined but bypassed by raw SQL in cascade_delete_flow

Reproduction

  1. Run Langflow against a PostgreSQL database.
  2. Apply all migrations up to and including 59a272d6669atrace.flow_id will have CASCADE,
    but span.trace_id will not.
  3. Execute at least one flow that generates traces (creates rows in trace and span).
  4. Delete that flow via the UI or API: DELETE /api/v1/flows/{flow_id}.
  5. Observe the 500 error and ForeignKeyViolation in the application logs.

Expected behavior

Deleting a flow should atomically remove all child records in dependency order without any
IntegrityError:

span → trace → flow

This is consistent with how other child tables (MessageTable, TransactionTable, etc.) are
already handled by cascade_delete_flow.

Who can help?

No response

Operating System

Openshift

Langflow Version

1.8.1

Python Version

3.12

Screenshot

No response

Flow File

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions