windmill-labs/windmill · error · Error

A ${existing.resource_type} resource already exists at ${pat

Error message

A ${existing.resource_type} resource already exists at ${path}. Pick another path.

What it means

persist() refuses to overwrite an existing resource at the target path when its resource_type is not 'ai_agent'. Because the drawer's path-uniqueness check is debounced, a fast save can race past it, so this server-side re-check prevents clobbering a resource of a different type.

Source

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

		// 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.
		const forkMarker = tools
		const savingEditPath = getAgentEditingPath(forkMarker)
		const exists = await ResourceService.existsResource({ workspace: ws!, path })
		if (exists) {
			// The drawer's path check is debounced, so a fast save can reach here with an unrelated
			// resource at the path — never clobber a resource of another type.
			const existing = await ResourceService.getResource({ workspace: ws!, path })
			if (existing.resource_type !== 'ai_agent') {
				throw new Error(
					`A ${existing.resource_type} resource already exists at ${path}. Pick another path.`
				)
			}
			await ResourceService.updateResourceValue({
				workspace: ws!,
				path,
				requestBody: { value }
			})
		} else {
			await ResourceService.createResource({
				workspace: ws!,
				requestBody: {
					path,
					value,
					resource_type: 'ai_agent',
					description: description || 'Reusable AI agent'
				}
			})

View on GitHub (pinned to e474e8803c)

Solutions

  1. Pick a different path that is not already occupied by another resource type
  2. Wait for the drawer's debounced path-availability check to clear before saving
  3. Delete/rename the conflicting resource at that path if it is no longer needed

Example fix

// before
await persist(newPath)
// after
const exists = await ResourceService.existsResource({ workspace: ws, path: newPath })
if (exists) {
  sendUserToast('Path already in use by another resource, pick another path', true)
  return
}
await persist(newPath)
Defensive patterns

Strategy: validation

Validate before calling

const exists = await ResourceService.existsResource({ workspace: ws, path })
if (exists) {
  const r = await ResourceService.getResource({ workspace: ws, path })
  if (r.resource_type !== 'ai_agent') { sendUserToast('Path taken by another resource type', true); return }
}

Type guard

function isFreeAgentPath(r: { resource_type: string } | undefined): boolean { return !r || r.resource_type === 'ai_agent' }

Try / catch

try { await persist(path) } catch (e) { if (e.message.includes('already exists')) { sendUserToast(e.message, true); focusPathInput() } else { throw e } }

Prevention

When it happens

Trigger: ResourceService.existsResource returns true for `path`, and the fetched existing resource's resource_type differs from 'ai_agent' — the chosen path collides with, e.g., a variable, another resource kind, or a script path.

Common situations: User types a path already used by another resource; debounced availability check hadn't completed before Save was clicked; renaming an agent onto an existing non-agent path.

Related errors


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