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
- Use write_app_file / write_app_runnable to modify the existing draft instead of write_app.
- Delete the existing app draft first, then call write_app again.
- 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
- Track which app paths already have drafts in the session before calling write_app.
- Treat write_app as create-only; use file/runnable tools for subsequent edits.
- Delete stale drafts before regenerating an app from scratch.
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
- An app already exists at "${path}". Use read_workspace_item
- This forked workspace '${workspaceId}' (${workspaceName}) al
- inlineScript is required when runnable type is "inline".
- path is required when runnable type is "script", "flow", or
- "${filePath}" is generated automatically from backend runnab
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/5a943bb88a22f54b.
Report an issue: GitHub.