Unable to Delete Flows
Bug Description
Whenever I try to delete a flow, there is an exception and I am unable to delete the flow.
Reproduction
Create a flow, add some components to it, run it a few times (send data using playground). Try to delete it.
Expected behavior
It should be deletable.
Who can help?
@italojohnny
Operating System
RHEL 8 Langflow Docker image
Langflow Version
1.0.14
Python Version
None
Screenshot
No response
Flow File
No response
The logs are not being printed properly for some reason, but here is the most relevant part. If I had to guess, attempt to delete a flow is cascading NULL to other tables, however, there is a not-null constrain on them. So, they should either be cascade delete or nullable.
NotNullViolation: null value in column
"flow_id" of relation "transaction"
violates not-null constraint
DETAIL: Failing row contains
(2024-08-06 10:28:46.131792,
CustomComponent-OBvDV,
ChatOutput-rzfAn, {"code": "# from
langflow.field_typing import
Data\nfrom langflo..., {"results": {},
"artifacts": {"success_result":
{"repr": "{\n ..., success, null,
null,
059c084a-51c5-4465-9e41-a34cef3e1a0e).
It looks like the issue you're encountering when trying to delete a flow is due to a NotNullViolation exception. This happens because deleting the flow is causing NULL values to be cascaded to other tables that have not-null constraints.
To resolve this, you need to ensure that related records are properly handled before deleting the flow. This can be done by either setting the related records to a default value or deleting them as well. Here is an example of how you might modify the delete_flow function to handle related records:
from fastapi import HTTPException, Depends
from sqlmodel import Session, select
from uuid import UUID
from langflow.services.database.models.flow import Flow
from langflow.services.database.models.user.model import User
from langflow.services.deps import get_session, get_settings_service
from langflow.api.v1.flows import read_flow
def delete_flow(
*,
session: Session = Depends(get_session),
flow_id: UUID,
current_user: User = Depends(get_current_active_user),
settings_service=Depends(get_settings_service),
):
"""Delete a flow."""
flow = read_flow(
session=session,
flow_id=flow_id,
current_user=current_user,
settings_service=settings_service,
)
if not flow:
raise HTTPException(status_code=404, detail="Flow not found")
# Handle related records before deleting the flow
# Example: If there are related records in another table, handle them here
# related_records = session.exec(select(RelatedModel).where(RelatedModel.flow_id == flow_id)).all()
# for record in related_records:
# session.delete(record)
session.delete(flow)
session.commit()
return {"message": "Flow deleted successfully"}
Make sure to replace the commented section with the actual handling of related records in your database schema. This ensures that when a flow is deleted, any related records are either deleted or updated to prevent NotNullViolation exceptions [1][2].
Additionally, ensure that your database schema is correctly set up to handle these relationships. For example, the Alembic migration script provided shows how to add and remove columns and constraints, which can be useful for managing foreign key relationships and ensuring data integrity:
def upgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn)
tables = inspector.get_table_names()
try:
if "flowstyle" in tables:
op.drop_table("flowstyle")
with op.batch_alter_table("flow", schema=None) as batch_op:
flow_columns = [column["name"] for column in inspector.get_columns("flow")]
if "is_component" not in flow_columns:
batch_op.add_column(sa.Column("is_component", sa.Boolean(), nullable=True))
if "updated_at" not in flow_columns:
batch_op.add_column(sa.Column("updated_at", sa.DateTime(), nullable=True))
if "folder" not in flow_columns:
batch_op.add_column(sa.Column("folder", sqlmodel.sql.sqltypes.AutoString(), nullable=True))
if "user_id" not in flow_columns:
batch_op.add_column(sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=True))
indices = inspector.get_indexes("flow")
indices_names = [index["name"] for index in indices]
if "ix_flow_user_id" not in indices_names:
batch_op.create_index(batch_op.f("ix_flow_user_id"), ["user_id"], unique=False)
if "fk_flow_user_id_user" not in indices_names:
batch_op.create_foreign_key("fk_flow_user_id_user", "user", ["user_id"], ["id"])
except Exception:
pass
def downgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn)
try:
if "flowstyle" not in inspector.get_table_names():
op.create_table(
"flowstyle",
sa.Column("color", sa.String(), nullable=False),
sa.Column("emoji", sa.String(), nullable=False),
sa.Column("flow_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.ForeignKeyConstraint(["flow_id"], ["flow.id"]),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("id"),
)
with op.batch_alter_table("flow", schema=None) as batch_op:
flow_columns = [column["name"] for column in inspector.get_columns("flow")]
if "user_id" in flow_columns:
batch_op.drop_column("user_id")
if "folder" in flow_columns:
batch_op.drop_column("folder")
if "updated_at" in flow_columns:
batch_op.drop_column("updated_at")
if "is_component" in flow_columns:
batch_op.drop_column("is_component")
indices = inspector.get_indexes("flow")
indices_names = [index["name"] for index in indices]
if "ix_flow_user_id" in indices_names:
batch_op.drop_index("ix_flow_user_id")
constraints = inspector.get_foreign_keys("flow")
constraint_names = [constraint["name"] for constraint in constraints]
if "fk_flow_user_id_user" in constraint_names:
batch_op.drop_constraint("fk_flow_user_id_user", type_="foreignkey")
except Exception as e:
print(f"Error during downgrade: {e}")
This script shows how to manage columns and constraints, which can help ensure that your database schema supports the necessary relationships and constraints to avoid NotNullViolation exceptions [3].
For further assistance, @ogabrielluiz, your expertise would be greatly appreciated here.
To continue talking to Dosu, mention @dosu.
I'm getting the same issue. What is the fix for this?
Is anybody else experiencing this?
Same issue for me..
Any solutions?
I've worked around this by stopping Langflow, going into the sqlite database command line, and deleting:
delete from 'transaction' where flow_id = 'e57cdc407439432cb448d7b4a5f1eb59';
delete from flow where id = 'e57cdc407439432cb448d7b4a5f1eb59';
But it definitely looks to be a problem in 1.0.14 (not sure what other versions affected)
You can find the SQLite path with --log-level=debug
This issue does not reproduce on v1.0.15 (just released).
Yes, my issue is gone in the latest release. Thank you!