windmill-labs/windmill · error

Draft ${type} "${path}" has no value to deploy.

Error message

Draft ${type} "${path}" has no value to deploy.

What it means

Thrown by the chat's deployDraft tool when a draft exists for the item (getGlobalDraft returned a record) but its value field is undefined — the draft shell exists without any content to publish. Deploying an undefined value would create or overwrite a workspace item with nothing, so the tool refuses. This is distinct from 'No draft found': the draft slot exists but was never filled (or was cleared).

Source

Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:7267

	const { workspace, toolId, toolCallbacks, sessionId } = ctx
	const {
		type,
		path,
		trigger_kind: triggerKind,
		deployment_message: deploymentMessage,
		force
	} = args

	if (type === 'trigger' && !triggerKind) {
		throw new Error('trigger_kind is required when deploying a trigger.')
	}

	const draft = await getGlobalDraft(workspace, type, path, triggerKind)
	if (!draft) {
		throw new Error(`No draft found for ${type} "${path}".`)
	}
	if (draft.value === undefined) {
		throw new Error(`Draft ${type} "${path}" has no value to deploy.`)
	}

	toolCallbacks.setToolStatus(toolId, {
		content: `Deploying ${type} "${path}"...`
	})

	let actions: ToolDisplayAction[] | undefined
	// Where the deploy actually lands — the app branch can resolve a different
	// target from the draft's own path fields; the mask rename below must track it.
	let deployedPath = path
	// Appended to the success message when the deploy deliberately left something alone,
	// so "deployed" is never read as "everything in the draft was applied".
	let deployNote: string | undefined

	if (type === 'script' || type === 'flow') {
		// Promote the full persisted draft via the shared deploy module — the same
		// "promote a draft to deployed" code the compare page's Review & Deploy uses.
		// It deploys every field of the draft; the previous local builders dropped

View on GitHub (pinned to e474e8803c)

Solutions

  1. Reload the item's draft in the editor/chat so the value is populated, then deploy again.
  2. If the draft is genuinely empty, edit the content before deploying.
  3. Clear the local draft and recreate it if the state appears corrupted.

Example fix

// before
const draft = await getGlobalDraft(ws, 'app', 'a/dashboard') // exists, value undefined
await deployDraft({ type: 'app', path: 'a/dashboard' })
// Error: Draft app "a/dashboard" has no value to deploy.

// after: wait for the draft content to load (or set it) before deploying
await waitForDraftValue('app', 'a/dashboard')
await deployDraft({ type: 'app', path: 'a/dashboard' })
Defensive patterns

Strategy: validation

Validate before calling

const draft = await getGlobalDraft(workspace, type, path, triggerKind)
if (draft && draft.value === undefined) {
  throw new Error(`draft ${type} "${path}" has not loaded its content yet`)
}
await deployDraft({ type, path, workspace })

Type guard

function draftHasValue(draft) {
  return draft != null && draft.value !== undefined
}

Try / catch

try {
  await deployDraft(args)
} catch (e) {
  if (/has no value to deploy/.test(e.message)) {
    // reload the draft content, then retry
  } else throw e
}

Prevention

When it happens

Trigger: Calling the deploy tool when draft.value === undefined for the located draft — e.g. a draft record was created by opening the item but its content never loaded/was wiped by a failed sync or a clearing operation.

Common situations: A race where the draft is created before its content fetch resolves and the user (or agent) deploys immediately; a draft reset/cleared by conflict handling; corrupted local IndexedDB draft state after a crashed session.

Related errors


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