windmill-labs/windmill · error
Cannot deploy ${label}: saving the latest draft failed (${fa
Error message
Cannot deploy ${label}: saving the latest draft failed (${failureMessage ?? 'unknown error'}). Retry once the draft saves. What it means
Thrown by flushDraftOrThrow when, after flushing the draft to the server, UserDraftDbSyncer.getState(query) reports state 'failed'. This means the draft save itself failed (e.g. validation or a network/server error), and deploying would ship a stale or unknown version. The failureMessage from the syncer, when present, is embedded in the error; otherwise 'unknown error' is used. The deploy is blocked until the draft saves successfully.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:7233
// Flush a draft's pending editor autosave, then verify it actually landed before
// the caller re-reads the persisted draft. `flush()` resolves even when the save
// recorded a conflict (server has a newer version) or failed (network/5xx) — it
// does not throw — so without this check a deploy could publish a stale/conflicting
// draft. Abort with a clear message instead.
async function flushDraftOrThrow(
query: Parameters<typeof UserDraftDbSyncer.flush>[0],
label: string
): Promise<void> {
await UserDraftDbSyncer.flush(query)
if (UserDraftDbSyncer.getConflict(query).conflict) {
throw new Error(
`Cannot deploy ${label}: the draft has a conflicting newer version on the server. Open it in the editor and resolve the conflict first.`
)
}
const { state, failureMessage } = UserDraftDbSyncer.getState(query)
if (state === 'failed') {
throw new Error(
`Cannot deploy ${label}: saving the latest draft failed (${failureMessage ?? 'unknown error'}). Retry once the draft saves.`
)
}
}
async function deployDraft(
args: {
type: WorkspaceItemType
path: string
trigger_kind?: TriggerKind
deployment_message?: string
force?: boolean
},
ctx: WriteDraftCtx
): Promise<string> {
const { workspace, toolId, toolCallbacks, sessionId } = ctx
const {
type,View on GitHub (pinned to e474e8803c)
Solutions
- Read the embedded failureMessage to see why the save failed and fix that cause (e.g. restore network, fix validation).
- Retry the deploy once the draft saves successfully (the error explicitly says to wait for the save to complete).
- If the save keeps failing, open the draft in the editor and re-save manually to see the full server error.
Example fix
// before
await deployDraft({ type: 'flow', path: 'f/etl' })
// Error: Cannot deploy flow "f/etl": saving the latest draft failed (network timeout). Retry once the draft saves.
// after: reconnect / fix the save, confirm draft state is 'saved', then retry
await deployDraft({ type: 'flow', path: 'f/etl' }) // succeeds Defensive patterns
Strategy: retry
Validate before calling
import { UserDraftDbSyncer } from '$lib/components/copilot/chat/global/userDraftSyncer'
const { state } = UserDraftDbSyncer.getState(query)
if (state !== 'saved') {
// wait for the draft save to settle before deploying
} Type guard
function isDraftSaveFailed(query) {
const { state } = UserDraftDbSyncer.getState(query)
return state === 'failed'
} Try / catch
try {
await deployDraft(args)
} catch (e) {
if (/saving the latest draft failed/.test(e.message)) {
// check e.message for failureMessage, fix connectivity/validation, then retry
} else throw e
} Prevention
- Ensure a stable connection before running agent-driven deploys.
- Check the draft save state before issuing a deploy.
- Address the failureMessage cause (validation, permissions) before retrying.
When it happens
Trigger: Calling the chat's deploy tool when the pending draft flush failed: UserDraftDbSyncer.getState(query).state === 'failed' after UserDraftDbSyncer.flush(query), with an optional failureMessage carried on the sync state.
Common situations: The browser lost connectivity mid-save; the server rejected the draft save (schema/validation failure); a transient 5xx during autosave; the item was deleted server-side while a draft existed locally.
Related errors
- Cannot deploy ${label}: the draft has a conflicting newer ve
- ApiError with mapped HTTP status message (e.g. "Not Found",
- Generic Error: status: ${errorStatus}; status text: ${errorS
- Dependency generation failed: ${queueResponse.status} ${queu
- Failed to poll dependencies job ${jobId}: ${e?.message ?? e}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/451cf1177c5a2d80.
Report an issue: GitHub.