windmill-labs/windmill · error · Error

A draft for app "${path}" already exists. Use write_app_file

Error message

A draft for app "${path}" already exists. Use write_app_file / write_app_runnable to modify it, or delete the existing draft first.

What it means

The write_app tool creates a NEW app draft and refuses when a draft for the same app path already exists in the global draft store. Because existing drafts must be modified incrementally via write_app_file / write_app_runnable, blindly overwriting would clobber prior edits, so the tool throws with instructions.

Source

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

		detachAfterMs: waitSecondsToDetachMs(args.wait_seconds),
		loadScript: loadScriptForFlowStep,
		loadFlowPreviewValue: loadDraftFlowPreviewValue
	})
}

async function initApp(
	args: {
		path: string
		summary?: string
		framework: FrameworkKey
	},
	ctx: WriteDraftCtx
): Promise<string> {
	const { workspace, toolId, toolCallbacks } = ctx
	const { path, summary, framework } = args

	if (await getGlobalDraft(workspace, 'app', path)) {
		throw new Error(
			`A draft for app "${path}" already exists. Use write_app_file / write_app_runnable to modify it, or delete the existing draft first.`
		)
	}
	if (await AppService.existsApp({ workspace, path })) {
		throw new Error(
			`An app already exists at "${path}". Use read_workspace_item + write_app_file / write_app_runnable to modify it.`
		)
	}

	toolCallbacks.setToolStatus(toolId, {
		content: `Saving app "${path}" draft (${framework} template)…`
	})

	const template = FRAMEWORK_TEMPLATES[framework]
	const value: AppDraftValue = {
		summary,
		files: { ...template },
		runnables: { [STARTER_RUNNABLE_KEY]: { ...STARTER_RUNNABLE } }

View on GitHub (pinned to e474e8803c)

Solutions

  1. Use write_app_file / write_app_runnable to modify the existing draft instead of write_app.
  2. Delete the existing app draft first, then call write_app again.
  3. Use a different app path if a genuinely separate app was intended.

Example fix

// before
writeApp({ path: 'f/newapp', framework: 'svelte' }) // second call, draft exists
// after
writeAppFile({ path: 'f/newapp', filePath: 'App.svelte', content: '...' })
Defensive patterns

Strategy: try-catch

Validate before calling

if (await getGlobalDraft(workspace, 'app', path)) {
  // modify via write_app_file / write_app_runnable instead of write_app
}

Try / catch

try {
  await writeApp(args)
} catch (e) {
  if (e.message.includes('already exists')) {
    await writeAppFile({ path: args.path, filePath: 'App.svelte', content: newContent })
  } else throw e
}

Prevention

When it happens

Trigger: Calling the write_app tool when getGlobalDraft(workspace, 'app', path) returns an existing draft for that path.

Common situations: Re-running write_app after an earlier draft-creation call in the same chat session; the model retries the create call after a partial failure; the user asks to regenerate an app whose draft already exists.

Related errors


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