windmill-labs/windmill · error · Error

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

Error message

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

What it means

read_workspace_item / script-reading logic finds a draft script in the global draft store but the draft lacks a usable content string or a language. Rather than returning a half-written draft, the code throws so the model gets a clear signal the draft is incomplete. It guards the invariant that a readable script draft has both content and a ScriptLang.

Source

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

function writeVariableDraft(args: WriteVariableArgs, ctx: WriteDraftCtx): Promise<string> {
	// A variable's value is never interpolated, so "$var:<its own path>" as the value
	// is always the model echoing the reference syntax back instead of a real value.
	if (args.value === `$var:${args.path}`) {
		throw new Error(
			`"${args.value}" is not a valid value for variable "${args.path}" — it is a self-reference. Omit value to keep the current one.`
		)
	}
	return writeDraft(VARIABLE_SPEC, 'variable', args.path, args, ctx, { override: args.override })
}

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

async function editScript(
	args: { path: string; old_string: string; new_string: string; replace_all: boolean },
	ctx: WriteDraftCtx
): Promise<string> {
	const { path, old_string: oldString, new_string: newString, replace_all: replaceAll } = args
	ctx.toolCallbacks.setToolStatus(ctx.toolId, { content: `Editing script "${path}"...` })

	const base = await loadScriptForEdit(path, ctx.workspace)
	const updated = findAndReplace(base.content, oldString, newString, replaceAll, 'script source')
	return writeScriptDraft(
		{

View on GitHub (pinned to e474e8803c)

Solutions

  1. Delete the incomplete draft (delete draft for that script path) and re-run the read to fall back to the deployed script.
  2. Re-run write_script with both content and language to complete the draft, then read again.
  3. Read the deployed script via the normal script-by-path service call if you did not intend to work on a draft.

Example fix

// before (draft stored incomplete)
writeScript({ path: 'f/x', summary: 'todo' }) // no content/language
// after
writeScript({ path: 'f/x', content: 'export async function main() {}', language: 'python3' })
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

try {
  return await readScript(path)
} catch (e) {
  if (String(e.message).includes('missing content or language')) {
    await deleteDraft('script', path)
    return await ScriptService.getScriptByPath({ workspace, path })
  }
  throw e
}

Prevention

When it happens

Trigger: getGlobalDraft(workspace, 'script', path) returns a draft whose `value` is not a string or whose `language` is falsy — e.g. a draft created by a failed/partial write_script call or cleared content.

Common situations: A previous write_script tool call stored only metadata (language or summary) without content; a draft was created by template scaffolding that never filled the body; concurrent tool calls left the draft in a partial state.

Related errors


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