{"record":{"id":"5c9020e34cf87eec","repo":"mastra-ai/mastra","slug":"connection-connectionid-is-still-pinned-by-us","errorCode":null,"errorMessage":"Connection ${connectionId} is still pinned by ${usage} agent(s). Pass ?force=true to disconnect anyway.","messagePattern":"Connection (.+?) is still pinned by (.+?) agent\\(s\\)\\. Pass \\?force=true to disconnect anyway\\.","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"packages/server/src/server/handlers/tool-providers.ts","lineNumber":628,"sourceCode":"      // against another tenant's connectionId by guessing it.\n      if (store && !matched && !isAdmin) {\n        throw new HTTPException(403, {\n          message: 'You do not have permission to disconnect this connection',\n        });\n      }\n\n      const effectiveOwner = ownerAuthorId ?? callerAuthorId;\n      const isShared = ownerScope === 'shared';\n      if (!isShared && effectiveOwner !== callerAuthorId && !isAdmin) {\n        throw new HTTPException(403, {\n          message: 'You do not have permission to disconnect this connection',\n        });\n      }\n\n      if (!isForce) {\n        const usage = await countConnectionUsage(mastra, connectionId);\n        if (usage > 0) {\n          throw new HTTPException(409, {\n            message: `Connection ${connectionId} is still pinned by ${usage} agent(s). Pass ?force=true to disconnect anyway.`,\n          });\n        }\n      }\n\n      let revoked = false;\n      if (provider.capabilities?.supportsRevoke && typeof provider.revokeConnection === 'function') {\n        await provider.revokeConnection(connectionId);\n        revoked = true;\n      }\n\n      if (store) {\n        await store.deleteConnection({\n          authorId: effectiveOwner,\n          providerId: provider.info.id,\n          connectionId,\n        });\n      }","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/tool-providers.ts#L610-L646","documentation":"Thrown when a DELETE request to disconnect a tool provider connection is made without `?force=true` while the connection is still referenced by one or more agents. The server counts references via countConnectionUsage and refuses to silently break agents that depend on the connection. This is an intentional safety guard (HTTP 409) against orphaning agent tool configs.","triggerScenarios":"DELETE /api/tool-providers/:providerId/connections/:connectionId without the force query parameter, while countConnectionUsage(mastra, connectionId) returns > 0 agents referencing that connection.","commonSituations":"Disconnecting an OAuth/provider connection that is still attached to agents via toolsResolver; cleanup scripts removing connections in shared environments; stale connections still wired into agent definitions after a migration.","solutions":["Re-call the disconnect endpoint with ?force=true to force removal.","Remove the connection from all agents referencing it (inspect agents' tool/provider configs), then retry without force.","Use the connection usage endpoint to list the pinning agents before deciding."],"exampleFix":"// before\nawait fetch(`/api/tool-providers/${providerId}/connections/${connectionId}`, { method: 'DELETE' });\n// after\nawait fetch(`/api/tool-providers/${providerId}/connections/${connectionId}?force=true`, { method: 'DELETE' });","handlingStrategy":"try-catch","validationCode":"const usage = await api.get(`/api/tool-providers/${providerId}/connections/${connectionId}/usage`);\nconst isPinned = usage.agents.length > 0;\nif (isPinned && !confirmForce) throw new Error(`Connection still used by ${usage.agents.length} agent(s)`);","typeGuard":null,"tryCatchPattern":"try {\n  await api.delete(`/api/tool-providers/${p}/connections/${id}`);\n} catch (e) {\n  if (e.status === 409) await api.delete(`/api/tool-providers/${p}/connections/${id}?force=true`);\n  else throw e;\n}","preventionTips":["Check usage via the usage endpoint before disconnecting.","Detatch connections from agents as part of offboarding cleanup.","Treat 409 as an expected signal, not a crash."],"tags":["http-409","tool-providers","conflict","api"],"backgroundTag":"resource-still-in-use-conflict","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}