windmill-labs/windmill · error
draft has no content/language
Error message
draft ${path} has no content/language What it means
When running a pipeline node from the editor, drafts are preferred as the runnable input. If the editor mode expects drafts (edit mode or drafts included) and a draft exists for the path but its script is missing content or language, the page throws this error rather than calling runScriptPreview with an invalid empty request.
Solutions
- Open the draft and save actual script content (and ensure language is set) before running
- Delete the empty/stale draft and re-create it from the step
- Run in non-edit mode without drafts so the page falls back to the deployed script instead of the draft
- Guard before run: check draft.script.content && draft.script.language
Example fix
// before await runNode(path) // after const d = pe.drafts.get(path) if (d && d.script.content && d.script.language) await runNode(path)
Defensive patterns
Strategy: validation
Validate before calling
const d = pe.drafts.get(path);
if (d && (!d.script.content || !d.script.language)) {
throw new Error(`draft ${path} has no content/language`);
} Type guard
function isRunnableDraft(d: unknown): d is { script: { content: string; language: string } } {
const x = d as any;
return !!x?.script?.content && !!x?.script?.language;
} Try / catch
try { await runNode(path); } catch (e) {
if (e.message.includes('has no content/language')) { openDraftEditor(path); }
else throw e;
} Prevention
- Never create placeholder drafts without content before enabling run
- Save drafts with both content and language set before running
- Fall back to the deployed script when the draft is incomplete
- Guard runScriptPreview calls with a content/language check
When it happens
Trigger: Clicking run on a script node whose staged draft has empty script.content or missing draft.script.language — e.g. a freshly created draft never saved with content, or a draft loaded incompletely.
Common situations: Creating an empty draft then hitting run; a draft synced from a failed load; includeDrafts enabled and a placeholder draft for a new path present; data-upload staging pointing at an unpopulated draft.
Related errors
- No local file for to infer its S3Object parameter.
- Pipeline node content must declare the pipeline annotation…
- Active instance not found in config
- AI agent modules "`).join(', ')} need a provider input…
- An app already exists at
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/200cf2359a568813.
Report an issue: GitHub.
Appendix: source
Thrown at frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte:1535
// mixed chain. Drafts run as previews of their local content; deployed
// scripts run their *saved* version (an open editor buffer with unsaved
// edits is not picked up — same as production dispatch).
async function launchCascadeScript(path: string): Promise<string> {
if (!$workspaceStore) throw new Error('no workspace')
// Only run draft content when the displayed graph actually includes
// drafts (same condition as `displayGraph`). Otherwise — View mode with
// drafts hidden — a bounded run must execute the *deployed* scripts the
// user is looking at, not preview jobs from hidden local drafts.
// Default a script's run args to its schedule's stored payload (the only
// trigger with static args — see resolveScheduleArgs), then let a
// data-upload entry's staged file override. runWholePipeline already
// refused to start unless every data-upload entry is staged, so this is
// always the intended input.
const staged = { ...(scheduleArgsByPath[path] ?? {}), ...(dataUploadArgs[path]?.args ?? {}) }
const draft = mode === 'edit' || includeDrafts ? pe.drafts.get(path) : undefined
if (draft) {
if (!draft.script.content || !draft.script.language) {
throw new Error(`draft ${path} has no content/language`)
}
return await JobService.runScriptPreview({
workspace: $workspaceStore,
requestBody: {
content: draft.script.content,
language: draft.script.language,
path,
args: { ...staged, _wmill_skip_asset_dispatch: true }
}
})
}
return await JobService.runScriptByPath({
workspace: $workspaceStore,
path,
requestBody: { ...staged, _wmill_skip_asset_dispatch: true }
})
}
// Poll a launched cascade job to a terminal state. Modest fixed cadence —View on GitHub (pinned to e474e8803c)