windmill-labs/windmill · error · Error
Runnable "${target.key}" language is ${expected}. Use backen
Error message
Runnable "${target.key}" language is ${expected}. Use backend/${target.key}/main.${expected}. What it means
Each inline runnable has a language derived from its stored inline script (via getInlineScriptExtension), and the tool target must supply a matching file extension. This error fires when the target's extension does not match the runnable's actual language, so file paths like backend/<key>/main.ts vs main.py must match.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:1781
* with a clear message otherwise.
*/
function getInlineRunnableContent(
value: AppDraftValue,
target: { kind: 'backend'; filePath: string; key: string; extension: 'ts' | 'py' },
appPath: string
): { content: string; runnable: PersistedRunnable } {
const runnable = value.runnables[target.key] as PersistedRunnable | undefined
if (!runnable) {
throw new Error(`Backend runnable "${target.key}" not found in app "${appPath}".`)
}
if (runnable.type !== 'inline' && runnable.type !== 'runnableByName') {
throw new Error(
`Runnable "${target.key}" is not inline. Use read_workspace_item on the referenced ${runnable.runType ?? 'item'} instead.`
)
}
const expected = getInlineScriptExtension(runnable)
if (target.extension !== expected) {
throw new Error(
`Runnable "${target.key}" language is ${expected}. Use backend/${target.key}/main.${expected}.`
)
}
return { content: runnable.inlineScript?.content ?? '', runnable }
}
async function loadAppValueForRead(path: string, workspace: string): Promise<AppDraftValue> {
const draft = await getGlobalDraft(workspace, 'app', path)
if (draft && draft.value && typeof draft.value === 'object' && 'files' in draft.value) {
return draft.value as AppDraftValue
}
const app = await AppService.getAppByPath({ workspace, path })
return appSourceToDraftValue(app, app)
}
async function loadAppDraftValue(path: string, workspace: string): Promise<LoadedAppDraftValue> {
const draft = await getGlobalDraft(workspace, 'app', path)View on GitHub (pinned to e474e8803c)
Solutions
- Check the runnable's inlineScript language and use the corresponding extension (e.g. main.ts, main.py)
- Retry the tool call with backend/<key>/main.<expected-extension>
Example fix
// before
runBackendFile('backend/myStep/main.py', code)
// after
runBackendFile('backend/myStep/main.ts', code) // matches the inline script's TypeScript Defensive patterns
Strategy: validation
Validate before calling
const expected = getInlineScriptExtension(runnable);
if (target.extension !== expected) throw new Error(`use main.${expected}`); Type guard
function matchesExtension(t, runnable) { return t.extension === getInlineScriptExtension(runnable); } Try / catch
try { writeFile(path, code) } catch (e) { if (e.message.includes('language is')) retryWithExtension(e.message.match(/main\.(\w+)/)?.[1]); } Prevention
- Derive the extension from the runnable, not from guesswork
- Always use backend/<key>/main.<ext> path convention
When it happens
Trigger: A tool call provides target.extension that differs from getInlineScriptExtension(runnable), e.g. writing backend/foo/main.py when the inline script is TypeScript.
Common situations: The model guesses the wrong language for an inline script; the runnable was re-authored in a different language than the model assumes.
Related errors
- Cannot infer script language from extension '${ext}' (file $
- Runnable "${target.key}" is not inline. Use read_workspace_i
- trigger_kind is required when type is trigger.
- describeDroppedScheduleOptions(dropped)
- Invalid config for a "${parsed.kind}" trigger. Call get_trig
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/afc0602b6f72cae4.
Report an issue: GitHub.