windmill-labs/windmill · error
delete_app_file only deletes frontend files. Use delete_app_
Error message
delete_app_file only deletes frontend files. Use delete_app_runnable for backend runnables.
What it means
The delete_app_file copilot tool only removes entries from the app's frontend files map. When the given file_path resolves to a backend runnable, it throws and points at delete_app_runnable, since runnables are stored in a separate map with their own lifecycle.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:5834
})
const { value } = await loadAppDraftValue(args.path, workspace)
value.files = { ...value.files, [target.filePath]: args.content }
const result = await saveAppDraft(workspace, args.path, value)
return finishAppDraftWrite(result, ctx, () => ({
content: `Updated ${target.filePath} in app "${args.path}"`,
message: `Updated draft app "${args.path}" with frontend file "${target.filePath}".`
}))
}
async function deleteAppFile(
args: { path: string; file_path: string },
ctx: WriteDraftCtx
): Promise<string> {
const { workspace, toolId, toolCallbacks } = ctx
const target = resolveAppFileTarget(args.file_path)
if (target.kind !== 'frontend') {
throw new Error(
`delete_app_file only deletes frontend files. Use delete_app_runnable for backend runnables.`
)
}
assertNotGeneratedAppFile(target.filePath)
toolCallbacks.setToolStatus(toolId, {
content: `Deleting ${target.filePath} from app "${args.path}"...`
})
const { value } = await loadAppDraftValue(args.path, workspace)
if (!(target.filePath in value.files)) {
throw new Error(`Frontend file "${target.filePath}" not found in app "${args.path}".`)
}
const { [target.filePath]: _removed, ...remaining } = value.files
value.files = remaining
const result = await saveAppDraft(workspace, args.path, value)
return finishAppDraftWrite(result, ctx, () => ({
content: `Removed ${target.filePath} from app "${args.path}"`,View on GitHub (pinned to e474e8803c)
Solutions
- Call delete_app_runnable with the runnable key instead
- Re-read the app to list actual frontend files before deleting
- Refresh the model's context with the current app schema
Example fix
// before
delete_app_file({ path: 'f/app_main', file_path: 'backend/legacy.py' })
// after
delete_app_runnable({ path: 'f/app_main', key: 'legacy' }) Defensive patterns
Strategy: validation
Validate before calling
const target = resolveAppFileTarget(args.file_path)
if (target.kind !== 'frontend') {
// route to delete_app_runnable instead
} Type guard
function isFrontendTarget(t: { kind: string }): t is { kind: 'frontend'; filePath: string } {
return t.kind === 'frontend'
} Try / catch
try {
await deleteAppFile(args)
} catch (e) {
if (e instanceof Error && e.message.includes('delete_app_runnable')) {
await deleteAppRunnable({ path: args.path, key: args.file_path })
}
} Prevention
- Classify the target before choosing the delete tool
- Refresh the app listing after renames/deletes so the model doesn't target stale entries
When it happens
Trigger: The AI model calls delete_app_file with a `file_path` that resolveAppFileTarget classifies as a backend runnable rather than a frontend file.
Common situations: Model tries to clean up by deleting what it thinks is a file but is actually an inline backend script; stale app schema in the model's context causes it to mistake runnables for files.
Related errors
- write_app_file only writes frontend files. Use write_app_run
- search_app requires a non-empty query.
- Frontend file "${target.filePath}" not found in app "${path}
- App "${path}" has no backend runnable "${key}". Available ru
- Backend runnable "${key}" not found in app "${path}".
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/4806045170a8c33a.
Report an issue: GitHub.