windmill-labs/windmill · error
A connection already exists at ${resourcePath}. Pick another
Error message
A connection already exists at ${resourcePath}. Pick another path. What it means
upsertSecretVariable backs each MCP connection with a resource at resourcePath. Before writing, it checks ResourceService.existsResource and throws if a connection already occupies that path, to avoid rotating the token of the existing connection and stranding it with a credential meant for the new one.
Source
Thrown at frontend/src/lib/components/mcp/secretVariable.ts:46
*/
export async function upsertSecretVariable(args: {
workspace: string
path: string
value: string
/** The MCP resource this token belongs to; stamped into the description. */
resourcePath: string
isOauth?: boolean
account?: number
}): Promise<void> {
const { workspace, path, value, resourcePath, isOauth, account } = args
const description = mcpTokenDescription(resourcePath)
// The path picker rejects an occupied path, but it validates on a debounce and
// this runs on the click: without the check, a fast save would rotate the token
// of the connection already living there and only then fail to create its
// resource, leaving that server holding a credential meant for another one.
if (await ResourceService.existsResource({ workspace, path: resourcePath })) {
throw new Error(`A connection already exists at ${resourcePath}. Pick another path.`)
}
if (await VariableService.existsVariable({ workspace, path })) {
const current = await VariableService.getVariable({ workspace, path, decryptSecret: false })
if (current.description !== description) {
throw new Error(`Variable at path ${path} already exists. Delete it or pick another path.`)
}
if (!isOauth) {
// `is_secret` is inherited from the row when omitted, so it is restated
// rather than assumed.
await VariableService.updateVariable({
workspace,
path,
requestBody: { value, is_secret: true }
})
return
}
await VariableService.deleteVariable({ workspace, path })View on GitHub (pinned to e474e8803c)
Solutions
- Pick a different resourcePath for the new connection and save again.
- Delete or rename the existing connection at that path first if it is no longer needed.
- Re-open the save dialog so the path picker re-validates with fresh data.
- If it is a stale collision, refresh the resource list — the occupier may have been deleted.
Example fix
// before
await upsertSecretVariable({ workspace, path: 'conn', resourcePath: 'mcp_conn' })
// after
await upsertSecretVariable({ workspace, path: 'conn2', resourcePath: 'mcp_conn2' }) Defensive patterns
Strategy: validation
Validate before calling
if (await ResourceService.existsResource({ workspace, path: resourcePath })) alert('Path taken — pick another') Type guard
function resourcePathIsFree(r) { return r === false } // existsResource returns boolean Try / catch
try { await upsertSecretVariable(args) } catch (e) { if (String(e.message).includes('already exists at')) promptNewPath(); else throw e } Prevention
- Let the path picker complete validation before enabling Save
- Use per-connection path prefixes (e.g. mcp_<name>) to avoid collisions
- Refresh resource list when saving after long idle periods
When it happens
Trigger: Saving an MCP connection (via OAuth finish, manual save, or resource creation) with a resourcePath that already has a resource — the debounced path-picker validation missed the collision between validation and click.
Common situations: Two connections configured in quick succession with the same path; renaming a connection to a path taken by another; race between two tabs saving simultaneously.
Related errors
- Variable at path ${path} already exists. Delete it or pick a
- The tool list for ${path} changed while it was loading. Try
- Database not found
- duplicate MCP tool name(s): {', '.join(duplicates)}
- MCP server does not publish OAuth authorization metadata
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/61cf73412fec548c.
Report an issue: GitHub.