windmill-labs/windmill · error · Error

"${args.value}" is not a valid value for variable "${args.pa

Error message

"${args.value}" is not a valid value for variable "${args.path}" — it is a self-reference. Omit value to keep the current one.

What it means

The AI chat's write_variable tool throws this when the model passes the literal string "$var:<same path>" as the new value of the variable it is updating. A Windmill variable's value is never interpolated, so such a value is always the model echoing the reference syntax back rather than a real value. The tool rejects it and asks the model to omit the value field to keep the current one.

Source

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

	ctx: WriteDraftCtx
): Promise<string> {
	return writeDraft(RESOURCE_SPEC, 'resource', args.path, args, ctx, { override: args.override })
}

const VARIABLE_SPEC: WriteSpec<VariableDraftState, WriteVariableArgs> = {
	probe: (workspace, path) => VariableService.existsVariable({ workspace, path }),
	fetchDeployed: async (workspace, path) =>
		variableToDraftState(
			await VariableService.getVariable({ workspace, path, decryptSecret: false })
		),
	buildDraft: (base, args) => createVariableToDraftState(args, base)
}

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 })

View on GitHub (pinned to e474e8803c)

Solutions

  1. Omit the `value` argument in the write_variable call so the current value is kept.
  2. If a real new value is intended, pass the actual value string instead of the $var: reference syntax.
  3. If the goal is to reference another item, use the correct resource/reference type (e.g. resource) instead of a variable self-reference.

Example fix

// before
writeVariable({ path: 'f/my/var', value: '$var:f/my/var' })
// after
writeVariable({ path: 'f/my/var' }) // omit value to keep current one
// or
writeVariable({ path: 'f/my/var', value: 'actual-value' })
Defensive patterns

Strategy: validation

Validate before calling

if (args.value === `$var:${args.path}`) {
  // skip sending value; keep current variable value
  delete args.value
}

Type guard

function isSelfReference(value: unknown, path: string): boolean {
  return typeof value === 'string' && value === `$var:${path}`
}

Try / catch

try {
  await writeVariable(args)
} catch (e) {
  if (String(e.message).includes('self-reference')) {
    await writeVariable({ ...args, value: undefined })
  } else throw e
}

Prevention

When it happens

Trigger: Calling the write_variable tool (writeVariableDraft) with args.value exactly equal to `$var:${args.path}`, where args.path is the same variable path being written.

Common situations: The model misreads a variable reference found in scripts/config as the variable's actual value and copies it back verbatim; user asks to 'keep the variable as its own reference'; hallucinated round-trip of interpolation syntax during multi-step edits.

Related errors


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