windmill-labs/windmill · error · Error

providerSaveError

Error message

providerSaveError

What it means

persist() in AgentResourceBar.svelte throws the current value of `providerSaveError` if it is set. That reactive variable holds a validation/config error for the agent (brain/provider) settings, blocking any save until the provider configuration is corrected.

Source

Thrown at frontend/src/lib/components/flows/content/AgentResourceBar.svelte:248

	// flow-local inputs survive linking, so a change to those must not block it.
	function discardedOnLinkSnapshot(): string {
		const brain: Record<string, unknown> = {}
		for (const key of AGENT_BRAIN_KEYS) {
			if (inputTransforms?.[key] !== undefined) {
				brain[key] = inputTransforms[key]
			}
		}
		return JSON.stringify([brain, tools])
	}

	// Create or update the `ai_agent` resource at `path` from the step's current brain + tools, then
	// link the step to it.
	// Returns false when the resource was written but the step was left alone, so callers can skip
	// the success toast that would otherwise bury the explanation.
	async function persist(path: string, description?: string): Promise<boolean> {
		const dropped = nonStaticBrainKeys(inputTransforms)
		if (providerSaveError) {
			throw new Error(providerSaveError)
		}
		if (dropped.length > 0) {
			sendUserToast(
				`Note: ${dropped.join(', ')} use a computed/connected value and won't be saved into the agent`,
				true
			)
		}
		// Tool inputs are saved verbatim: the agent carries its tools' default bindings (static, AI or
		// flow expressions) as authored. Host flows override per-step via tool_inputs, never here.
		const value = inputTransformsToAgentConfig(inputTransforms, tools)
		// The editor stays live during the requests below, so remember what linking would discard:
		// every brain transform and the tools. Comparing the saved config instead would miss a
		// non-static brain edit, which the resource cannot hold yet linking still strips.
		const savedSnapshot = discardedOnLinkSnapshot()
		// If the edit session ends or changes while the requests below are in flight (Cancel, undo,
		// session-draft sync, a different agent opened for editing), the resource is still written but
		// the step must not be relinked/cleared. Pinning the path — not merely "some edit is active" —
		// is what distinguishes this session from a replacement one.

View on GitHub (pinned to e474e8803c)

Solutions

  1. Fix the provider configuration shown in the AgentResourceBar (select a provider/model, resolve the displayed error)
  2. Clear the stale providerSaveError by re-validating the provider fields before saving
  3. Surface providerSaveError in the UI near the save button so users see why persist refuses to run

Example fix

// before
async function saveChanges() {
  await persist(path)
}
// after
async function saveChanges() {
  if (providerSaveError) {
    sendUserToast(providerSaveError, true)
    return
  }
  await persist(path)
}
Defensive patterns

Strategy: validation

Validate before calling

if (providerSaveError) { sendUserToast(providerSaveError, true); return }

Type guard

function canPersist(state: { providerSaveError?: string | null }): boolean { return !state.providerSaveError }

Try / catch

try { await persist(path) } catch (e) { sendUserToast(e.message, true) }

Prevention

When it happens

Trigger: saveChanges (or linked state) calls persist while providerSaveError is truthy — i.e. the provider/model/brain fields in the bar are in an invalid state (missing provider, invalid model name, unfilled required field).

Common situations: User edits input transforms but leaves the provider dropdown empty; a model name typo set providerSaveError earlier and was never cleared; async provider validation failed on mount.

Related errors


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