langgenius/dify · error · WorkflowInUseError

{str(e)}

Error message

{str(e)}

What it means

HTTP 400 from `PipelineWorkflowItemApi.delete`'s `WorkflowInUseError` branch. After passing the active-binding guard, the handler calls `WorkflowService.delete_workflow`; if the workflow is still referenced by an app (per `WorkflowInUseError`, a `ValueError` subclass defined in `services/errors/workflow_service.py`), the message is re-emitted as `abort(400, description=str(e))`.

Source

Thrown at api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py:788

    @get_rag_pipeline
    def delete(self, pipeline: Pipeline, workflow_id: str):
        """
        Delete a published workflow version that is not currently active on the pipeline.
        """
        if pipeline.workflow_id == workflow_id:
            abort(400, description=f"Cannot delete workflow that is currently in use by pipeline '{pipeline.id}'")

        workflow_service = WorkflowService()
        workflow_ref = WorkflowRefService.create_pipeline_workflow_ref(pipeline, workflow_id)

        with sessionmaker(db.engine).begin() as session:
            try:
                workflow_service.delete_workflow(
                    session=session,
                    workflow_ref=workflow_ref,
                )
            except WorkflowInUseError as e:
                abort(400, description=str(e))
            except DraftWorkflowDeletionError as e:
                abort(400, description=str(e))
            except ValueError as e:
                raise NotFound(str(e))

        return None, 204


@console_ns.route("/rag/pipelines/<uuid:pipeline_id>/workflows/published/processing/parameters")
class PublishedRagPipelineSecondStepApi(Resource):
    @console_ns.doc(params=query_params_from_model(NodeIdQuery))
    @console_ns.response(200, "Success", console_ns.models[RagPipelineStepParametersResponse.__name__])
    @setup_required
    @login_required
    @account_initialization_required
    @get_rag_pipeline
    @edit_permission_required
    @rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.DATASET_EDIT)

View on GitHub (pinned to ef8544b173)

Solutions

  1. Find and rebind or remove apps referencing the workflow before retrying the delete.
  2. Inspect `WorkflowInUseError`'s message (forwarded verbatim) to identify the referencing app.
  3. Sequence migrations so app rebinding completes before workflow cleanup.
  4. Retry idempotently after the referencing app is no longer bound.

Example fix

# before
delete workflow -> 400 WorkflowInUseError: referenced by app <id>
# after
# 1. update the app's workflow_ref to a different version
# 2. delete the now-unreferenced workflow
Defensive patterns

Strategy: try-catch

Validate before calling

from services.workflow_service import WorkflowService

# Before deleting, check whether any app references the workflow:
# WorkflowInUseError is raised inside WorkflowService.delete_workflow,
# so pre-check by listing apps bound to the workflow_ref.

Try / catch

try:
    client.delete(f"/rag/pipelines/{pid}/workflows/{wid}")
except HTTPError as err:
    if err.response.status_code == 400:
        # message forwarded from WorkflowInUseError — rebind/remove referencing app, then retry
        ...
    raise

Prevention

When it happens

Trigger: DELETE on a published workflow version that is still referenced by some app (the service raises `WorkflowInUseError`). Distinct from the active-pipeline guard (946) — this fires when *any* app still depends on the workflow.

Common situations: Deleting a workflow version that an app still points at; deleting too early during a migration before apps were rebound; concurrent publish/delete race where another flow bound an app to the workflow.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/dcf4b424b8e77782. Report an issue: GitHub.