{"record":{"id":"61cf73412fec548c","repo":"windmill-labs/windmill","slug":"a-connection-already-exists-at-resourcepath-pi","errorCode":null,"errorMessage":"A connection already exists at ${resourcePath}. Pick another path.","messagePattern":"A connection already exists at (.+?)\\. Pick another path\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/mcp/secretVariable.ts","lineNumber":46,"sourceCode":" */\nexport async function upsertSecretVariable(args: {\n\tworkspace: string\n\tpath: string\n\tvalue: string\n\t/** The MCP resource this token belongs to; stamped into the description. */\n\tresourcePath: string\n\tisOauth?: boolean\n\taccount?: number\n}): Promise<void> {\n\tconst { workspace, path, value, resourcePath, isOauth, account } = args\n\tconst description = mcpTokenDescription(resourcePath)\n\n\t// The path picker rejects an occupied path, but it validates on a debounce and\n\t// this runs on the click: without the check, a fast save would rotate the token\n\t// of the connection already living there and only then fail to create its\n\t// resource, leaving that server holding a credential meant for another one.\n\tif (await ResourceService.existsResource({ workspace, path: resourcePath })) {\n\t\tthrow new Error(`A connection already exists at ${resourcePath}. Pick another path.`)\n\t}\n\n\tif (await VariableService.existsVariable({ workspace, path })) {\n\t\tconst current = await VariableService.getVariable({ workspace, path, decryptSecret: false })\n\t\tif (current.description !== description) {\n\t\t\tthrow new Error(`Variable at path ${path} already exists. Delete it or pick another path.`)\n\t\t}\n\t\tif (!isOauth) {\n\t\t\t// `is_secret` is inherited from the row when omitted, so it is restated\n\t\t\t// rather than assumed.\n\t\t\tawait VariableService.updateVariable({\n\t\t\t\tworkspace,\n\t\t\t\tpath,\n\t\t\t\trequestBody: { value, is_secret: true }\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tawait VariableService.deleteVariable({ workspace, path })","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/mcp/secretVariable.ts#L28-L64","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait upsertSecretVariable({ workspace, path: 'conn', resourcePath: 'mcp_conn' })\n// after\nawait upsertSecretVariable({ workspace, path: 'conn2', resourcePath: 'mcp_conn2' })","handlingStrategy":"validation","validationCode":"if (await ResourceService.existsResource({ workspace, path: resourcePath })) alert('Path taken — pick another')","typeGuard":"function resourcePathIsFree(r) { return r === false } // existsResource returns boolean","tryCatchPattern":"try { await upsertSecretVariable(args) } catch (e) { if (String(e.message).includes('already exists at')) promptNewPath(); else throw e }","preventionTips":["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"],"tags":["mcp","path-conflict","resource"],"backgroundTag":"resource-path-already-exists","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"}