{"record":{"id":"e949e31c0d70ee5e","repo":"windmill-labs/windmill","slug":"providersaveerror","errorCode":null,"errorMessage":"providerSaveError","messagePattern":"providerSaveError","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/flows/content/AgentResourceBar.svelte","lineNumber":248,"sourceCode":"\t// flow-local inputs survive linking, so a change to those must not block it.\n\tfunction discardedOnLinkSnapshot(): string {\n\t\tconst brain: Record<string, unknown> = {}\n\t\tfor (const key of AGENT_BRAIN_KEYS) {\n\t\t\tif (inputTransforms?.[key] !== undefined) {\n\t\t\t\tbrain[key] = inputTransforms[key]\n\t\t\t}\n\t\t}\n\t\treturn JSON.stringify([brain, tools])\n\t}\n\n\t// Create or update the `ai_agent` resource at `path` from the step's current brain + tools, then\n\t// link the step to it.\n\t// Returns false when the resource was written but the step was left alone, so callers can skip\n\t// the success toast that would otherwise bury the explanation.\n\tasync function persist(path: string, description?: string): Promise<boolean> {\n\t\tconst dropped = nonStaticBrainKeys(inputTransforms)\n\t\tif (providerSaveError) {\n\t\t\tthrow new Error(providerSaveError)\n\t\t}\n\t\tif (dropped.length > 0) {\n\t\t\tsendUserToast(\n\t\t\t\t`Note: ${dropped.join(', ')} use a computed/connected value and won't be saved into the agent`,\n\t\t\t\ttrue\n\t\t\t)\n\t\t}\n\t\t// Tool inputs are saved verbatim: the agent carries its tools' default bindings (static, AI or\n\t\t// flow expressions) as authored. Host flows override per-step via tool_inputs, never here.\n\t\tconst value = inputTransformsToAgentConfig(inputTransforms, tools)\n\t\t// The editor stays live during the requests below, so remember what linking would discard:\n\t\t// every brain transform and the tools. Comparing the saved config instead would miss a\n\t\t// non-static brain edit, which the resource cannot hold yet linking still strips.\n\t\tconst savedSnapshot = discardedOnLinkSnapshot()\n\t\t// If the edit session ends or changes while the requests below are in flight (Cancel, undo,\n\t\t// session-draft sync, a different agent opened for editing), the resource is still written but\n\t\t// the step must not be relinked/cleared. Pinning the path — not merely \"some edit is active\" —\n\t\t// is what distinguishes this session from a replacement one.","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/flows/content/AgentResourceBar.svelte#L230-L266","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Fix the provider configuration shown in the AgentResourceBar (select a provider/model, resolve the displayed error)","Clear the stale providerSaveError by re-validating the provider fields before saving","Surface providerSaveError in the UI near the save button so users see why persist refuses to run"],"exampleFix":"// before\nasync function saveChanges() {\n  await persist(path)\n}\n// after\nasync function saveChanges() {\n  if (providerSaveError) {\n    sendUserToast(providerSaveError, true)\n    return\n  }\n  await persist(path)\n}","handlingStrategy":"validation","validationCode":"if (providerSaveError) { sendUserToast(providerSaveError, true); return }","typeGuard":"function canPersist(state: { providerSaveError?: string | null }): boolean { return !state.providerSaveError }","tryCatchPattern":"try { await persist(path) } catch (e) { sendUserToast(e.message, true) }","preventionTips":["Bind the Save button's disabled state to providerSaveError","Re-validate provider fields on every change and clear stale errors","Show the provider error next to the save control"],"tags":["validation","ai-agent","svelte"],"backgroundTag":"invalid-provider-config","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}