windmill-labs/windmill · error

draft ${path} 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.

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)

Solutions

  1. Open the draft and save actual script content (and ensure language is set) before running
  2. Delete the empty/stale draft and re-create it from the step
  3. Run in non-edit mode without drafts so the page falls back to the deployed script instead of the draft
  4. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/200cf2359a568813. Report an issue: GitHub.