windmill-labs/windmill · error
write_app_file only writes frontend files. Use write_app_run
Error message
write_app_file only writes frontend files. Use write_app_runnable to set inline backend script content.
What it means
The write_app_file copilot tool can only create/modify frontend files (svelte components and other files in the app's files map). When the target path resolves to a backend runnable (inline script), the tool throws and directs the model to use write_app_runnable instead, because backend code lives in the runnables map, not the files map.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:5808
)
}
toolCallbacks.setToolStatus(toolId, {
content: `Found ${totalMatchCount} match${totalMatchCount === 1 ? '' : 'es'} in ${fileCount} file${
fileCount === 1 ? '' : 's'
}`
})
return out.join('\n')
}
async function writeAppFile(
args: { path: string; file_path: string; content: string },
ctx: WriteDraftCtx
): Promise<string> {
const { workspace, toolId, toolCallbacks } = ctx
const target = resolveAppFileTarget(args.file_path)
if (target.kind !== 'frontend') {
throw new Error(
`write_app_file only writes frontend files. Use write_app_runnable to set inline backend script content.`
)
}
assertNotGeneratedAppFile(target.filePath)
toolCallbacks.setToolStatus(toolId, {
content: `Writing ${target.filePath} to app "${args.path}"...`
})
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}".`
}))
}
View on GitHub (pinned to e474e8803c)
Solutions
- Call write_app_runnable with the runnable key/path and the script content instead
- Re-read the app schema to identify which targets are frontend files vs backend runnables
- If the backend code should be a separate file rather than an inline script, restructure the app so the code lives in a frontend-accessible location or a dedicated script
Example fix
// before
write_app_file({ path: 'f/app_main', file_path: 'backend/process.py', content: 'def run(): ...' })
// after
write_app_runnable({ path: 'f/app_main', key: 'process', content: 'def run(): ...' }) Defensive patterns
Strategy: validation
Validate before calling
const target = resolveAppFileTarget(args.file_path)
if (target.kind !== 'frontend') {
// route to write_app_runnable instead
} Type guard
function isFrontendTarget(t: { kind: string }): t is { kind: 'frontend'; filePath: string } {
return t.kind === 'frontend'
} Try / catch
try {
await writeAppFile(args)
} catch (e) {
if (e instanceof Error && e.message.includes('write_app_runnable')) {
await writeAppRunnable({ path: args.path, key: args.file_path, content: args.content })
}
} Prevention
- Read the app's schema first to know which targets are files vs runnables
- Keep the model's context updated after each structural change
- Route backend-script edits through runnable tools by convention
When it happens
Trigger: The AI model calls write_app_file with `file_path` that resolveAppFileTarget classifies as backend (e.g. a `.py`/`.ts` runnable path like `backend/script.py` or a runnable key) instead of a frontend file path.
Common situations: Model confusion about the app's dual structure (files vs runnables); trying to edit the inline backend script of a runnable via the file-writing tool; misinterpreting the app schema returned by read tools.
Related errors
- delete_app_file only deletes frontend files. Use delete_app_
- 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/5a09cb8010488a0d.
Report an issue: GitHub.