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

  1. Read the embedded failureMessage to see why the save failed and fix that cause (e.g. restore network, fix validation).
  2. Retry the deploy once the draft saves successfully (the error explicitly says to wait for the save to complete).
  3. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/451cf1177c5a2d80. Report an issue: GitHub.