{"record":{"id":"742250e8578df7ef","repo":"windmill-labs/windmill","slug":"a-existing-resource-type-resource-already-exist","errorCode":null,"errorMessage":"A ${existing.resource_type} resource already exists at ${path}. Pick another path.","messagePattern":"A (.+?) resource already exists at (.+?)\\. Pick another path\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/flows/content/AgentResourceBar.svelte","lineNumber":275,"sourceCode":"\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.\n\t\tconst forkMarker = tools\n\t\tconst savingEditPath = getAgentEditingPath(forkMarker)\n\t\tconst exists = await ResourceService.existsResource({ workspace: ws!, path })\n\t\tif (exists) {\n\t\t\t// The drawer's path check is debounced, so a fast save can reach here with an unrelated\n\t\t\t// resource at the path — never clobber a resource of another type.\n\t\t\tconst existing = await ResourceService.getResource({ workspace: ws!, path })\n\t\t\tif (existing.resource_type !== 'ai_agent') {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`A ${existing.resource_type} resource already exists at ${path}. Pick another path.`\n\t\t\t\t)\n\t\t\t}\n\t\t\tawait ResourceService.updateResourceValue({\n\t\t\t\tworkspace: ws!,\n\t\t\t\tpath,\n\t\t\t\trequestBody: { value }\n\t\t\t})\n\t\t} else {\n\t\t\tawait ResourceService.createResource({\n\t\t\t\tworkspace: ws!,\n\t\t\t\trequestBody: {\n\t\t\t\t\tpath,\n\t\t\t\t\tvalue,\n\t\t\t\t\tresource_type: 'ai_agent',\n\t\t\t\t\tdescription: description || 'Reusable AI agent'\n\t\t\t\t}\n\t\t\t})","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/flows/content/AgentResourceBar.svelte#L257-L293","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pick a different path that is not already occupied by another resource type","Wait for the drawer's debounced path-availability check to clear before saving","Delete/rename the conflicting resource at that path if it is no longer needed"],"exampleFix":"// before\nawait persist(newPath)\n// after\nconst exists = await ResourceService.existsResource({ workspace: ws, path: newPath })\nif (exists) {\n  sendUserToast('Path already in use by another resource, pick another path', true)\n  return\n}\nawait persist(newPath)","handlingStrategy":"validation","validationCode":"const exists = await ResourceService.existsResource({ workspace: ws, path })\nif (exists) {\n  const r = await ResourceService.getResource({ workspace: ws, path })\n  if (r.resource_type !== 'ai_agent') { sendUserToast('Path taken by another resource type', true); return }\n}","typeGuard":"function isFreeAgentPath(r: { resource_type: string } | undefined): boolean { return !r || r.resource_type === 'ai_agent' }","tryCatchPattern":"try { await persist(path) } catch (e) { if (e.message.includes('already exists')) { sendUserToast(e.message, true); focusPathInput() } else { throw e } }","preventionTips":["Wait for the debounced path check to finish before enabling Save","Suggest a suffixed path (e.g. my_agent_2) on collision","Keep path availability checks server-authoritative at save time"],"tags":["path-collision","resources","race-condition"],"backgroundTag":"resource-path-conflict","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"}