windmill-labs/windmill · error · Error

Draft script "${moduleValue.path}" is missing content or lan

Error message

Draft script "${moduleValue.path}" is missing content or language.

What it means

When resolving a flow module's script content, a draft script for the module's path is checked first. If the draft exists but has no string content or no language, the tool throws instead of silently falling back, because the draft — not the deployed script — is the source of truth the model is editing. Deployed scripts are only read when no draft exists.

Source

Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:5340

	)
}

function normalizeTestRunArgs(args: Record<string, any> | null | undefined): Record<string, any> {
	return args ?? {}
}

function flowDraftValueForPreview(flowDraft: FlowDraftValue): FlowValue {
	return flowDraftAsEditableInput(flowDraft).value
}

async function loadScriptForFlowStep(
	moduleValue: { path: string; hash?: string },
	workspace: string
): Promise<{ content: string; language: ScriptLang }> {
	const draft = await getGlobalDraft(workspace, 'script', moduleValue.path)
	if (draft) {
		if (typeof draft.value !== 'string' || !draft.language) {
			throw new Error(`Draft script "${moduleValue.path}" is missing content or language.`)
		}
		return { content: draft.value, language: draft.language }
	}

	const script = moduleValue.hash
		? await ScriptService.getScriptByHash({ workspace, hash: moduleValue.hash })
		: await ScriptService.getScriptByPath({ workspace, path: moduleValue.path })
	return { content: script.content, language: script.language }
}

async function loadDraftFlowPreviewValue(
	path: string,
	workspace: string
): Promise<FlowValue | undefined> {
	if (!(await getGlobalDraft(workspace, 'flow', path))) {
		return undefined
	}
	const nestedFlow = await loadFlowDraftValue(path, workspace)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Complete the draft with write_script (content + language) for that path, then retry.
  2. Delete the incomplete draft so resolution falls back to the deployed script (by hash or path).
  3. Inline the script content into the flow module if it was meant to be a rawscript.

Example fix

// before
writeScript({ path: 'u/user/helper', summary: 'wip' }) // draft without content
// after
writeScript({ path: 'u/user/helper', content: 'export function main(){}', language: 'bun' })
Defensive patterns

Strategy: type-guard

Validate before calling

const draft = await getGlobalDraft(workspace, 'script', moduleValue.path)
if (draft && (typeof draft.value !== 'string' || !draft.language)) {
  await deleteGlobalDraft(workspace, 'script', moduleValue.path)
}

Type guard

function hasContentAndLang(d: unknown): d is { value: string; language: ScriptLang } {
  const x = d as any
  return !!x && typeof x.value === 'string' && typeof x.language === 'string'
}

Try / catch

try {
  return await loadModuleScript(moduleValue)
} catch (e) {
  if (String(e.message).includes('missing content or language')) {
    return ScriptService.getScriptByHash({ workspace, hash: moduleValue.hash! })
  }
  throw e
}

Prevention

When it happens

Trigger: getGlobalDraft(workspace, 'script', moduleValue.path) returns a draft with non-string value or missing language while loading a module's script for a flow inline-script operation.

Common situations: A partial write_script draft for the module's script path was left by an interrupted earlier call; the draft holds only a summary; the module points at a script path whose draft was created but never filled.

Related errors


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