langgenius/dify · warning · DraftWorkflowNotSync
draft_workflow_not_sync
draft_workflow_not_sync
Error message
Workflow graph might have been modified, please refresh and resubmit.
What it means
Returned (HTTP 409, error_code `draft_workflow_not_sync`, message 'Workflow graph might have been modified, please refresh and resubmit.') by POST /rag/pipelines/<pipeline_id>/workflows/draft when the supplied `hash` does not equal the current draft workflow's `unique_hash`. Dify uses optimistic concurrency: the client must echo the hash it last read so concurrent edits collide loudly rather than silently overwriting each other.
Source
Thrown at api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py:258
)
environment_variables = [
variable_factory.build_environment_variable_from_mapping(obj) for obj in environment_variables_list
]
conversation_variables_list = payload.conversation_variables or []
conversation_variables = [
variable_factory.build_conversation_variable_from_mapping(obj) for obj in conversation_variables_list
]
workflow = rag_pipeline_service.sync_draft_workflow(
pipeline=pipeline,
graph=payload.graph,
unique_hash=payload.hash,
account=current_user,
environment_variables=environment_variables,
conversation_variables=conversation_variables,
rag_pipeline_variables=payload.rag_pipeline_variables or [],
)
except WorkflowHashNotEqualError:
raise DraftWorkflowNotSync()
return {
"result": "success",
"hash": workflow.unique_hash,
"updated_at": TimestampField().format(workflow.updated_at or workflow.created_at),
}
@console_ns.route("/rag/pipelines/<uuid:pipeline_id>/workflows/draft/iteration/nodes/<string:node_id>/run")
class RagPipelineDraftRunIterationNodeApi(Resource):
@console_ns.expect(console_ns.models[NodeRunPayload.__name__])
@console_ns.response(200, "Success", console_ns.models[RagPipelineOpaqueResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@with_current_user
@get_rag_pipeline
@edit_permission_requiredView on GitHub (pinned to ef8544b173)
Solutions
- Re-fetch the draft workflow (GET .../workflows/draft), rebase the local graph onto the latest version, and re-submit with the new hash.
- Always pass the `hash` field returned by the most recent successful sync or GET.
- Surface a 'workflow was modified by another editor' dialog and offer to merge or overwrite.
- Serialize edits to a single pipeline to avoid concurrent sync collisions.
Example fix
// before
await post(`/rag/pipelines/${pid}/workflows/draft`, { graph: localGraph, hash: staleHash })
// after
async function syncDraft(graph) {
try {
return await post(`/rag/pipelines/${pid}/workflows/draft`, { graph, hash: currentHash })
} catch (e) {
if (e.code === 'draft_workflow_not_sync') {
const latest = await get(`/rag/pipelines/${pid}/workflows/draft`)
currentHash = latest.unique_hash
// rebase localGraph onto latest.graph, then retry once
return post(`/rag/pipelines/${pid}/workflows/draft`, { graph: rebased, hash: currentHash })
}
throw e
}
} Defensive patterns
Strategy: retry
Validate before calling
// before submitting, ensure the hash you hold is current
const latest = await get(`${base}/draft`)
if (latest.unique_hash !== localHash) {
// rebase localGraph onto latest.graph
localHash = latest.unique_hash
}
await post(`${base}/draft`, { graph: localGraph, hash: localHash }) Try / catch
try {
await post(`${base}/draft`, { graph, hash: currentHash })
} catch (e) {
if (e.code === 'draft_workflow_not_sync') {
const latest = await get(`${base}/draft`)
currentHash = latest.unique_hash
// rebase graph, then retry once
await post(`${base}/draft`, { graph: rebased, hash: currentHash })
} else throw e
} Prevention
- Always thread the `hash` from the most recent GET/sync response into the next POST.
- Detect `draft_workflow_not_sync` and offer a merge/overwrite choice.
- Serialize concurrent edits per pipeline to avoid repeated conflicts.
- Avoid long idles between load and save; refresh before save.
When it happens
Trigger: Two editors editing the same draft concurrently; submitting a graph built on a stale snapshot; the draft was modified by a background process (e.g., migration) between read and write; client did not pass the hash from its previous GET.
Common situations: Multi-user editing. Long idle time between load and save. Tab left open while another session published or restored. Frontend forgot to thread the hash from the last sync response.
Related errors
- Draft workflow not found, pipeline_id={pipeline.id}
- draft_workflow_not_exist
- draft_workflow_not_exist
- expected dict for file, got {type(raw_value)}
- expected list for files, got {type(raw_value)}
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/5bb1e4c78bf77754.
Report an issue: GitHub.