mastra-ai/mastra · warning · HTTPException
You do not have permission to disconnect this connection
Error message
You do not have permission to disconnect this connection
What it means
HTTP 403 fail-closed guard: when storage is configured and no stored connection row matches the requested connectionId, non-admin callers are refused before provider-side revokeConnection is invoked. This prevents an attacker from triggering revocation against another tenant's connectionId by guessing ids.
Source
Thrown at packages/server/src/server/handlers/tool-providers.ts:612
let ownerAuthorId: string | undefined;
let ownerScope: 'shared' | 'per-author' | 'caller-supplied' | undefined;
let matched = false;
if (store) {
const rows = await store.listConnectionsByAuthor({ providerId: provider.info.id });
const match = rows.find(r => r.connectionId === connectionId);
if (match) {
matched = true;
ownerAuthorId = match.authorId;
ownerScope = match.scope;
}
}
// Fail closed: if storage is configured and no row matches the
// requested connectionId, refuse the call for non-admins. Without
// this guard, a caller could trigger provider-side `revokeConnection`
// against another tenant's connectionId by guessing it.
if (store && !matched && !isAdmin) {
throw new HTTPException(403, {
message: 'You do not have permission to disconnect this connection',
});
}
const effectiveOwner = ownerAuthorId ?? callerAuthorId;
const isShared = ownerScope === 'shared';
if (!isShared && effectiveOwner !== callerAuthorId && !isAdmin) {
throw new HTTPException(403, {
message: 'You do not have permission to disconnect this connection',
});
}
if (!isForce) {
const usage = await countConnectionUsage(mastra, connectionId);
if (usage > 0) {
throw new HTTPException(409, {
message: `Connection ${connectionId} is still pinned by ${usage} agent(s). Pass ?force=true to disconnect anyway.`,
});View on GitHub (pinned to 75dd419e61)
Solutions
- Verify the connectionId matches a row visible in storage for this server (list connections via listConnections first).
- Ensure the same storage/database is configured on the server instance handling the disconnect.
- Re-sync or remove stale client-side connection references after rows were deleted.
- If the disconnect is legitimately cross-tenant (operations), perform it with admin credentials that pass hasAdminBypass.
Example fix
// before
await api.disconnect({ providerId: 'acme', connectionId: params.id }); // id from old cache
// after
const connections = await api.listConnections('acme');
if (connections.some(c => c.connectionId === params.id)) {
await api.disconnect({ providerId: 'acme', connectionId: params.id });
} Defensive patterns
Strategy: validation
Validate before calling
const connections = await api.listConnections(providerId);
if (!connections.some(c => c.connectionId === connectionId)) {
console.warn(`Connection ${connectionId} not in storage; skipping disconnect`);
return;
} Try / catch
try {
await api.disconnect({ providerId, connectionId });
} catch (e) {
if (e.status === 403 && /permission to disconnect/.test(e.message)) {
// refresh stored connections / notify user the connection is unknown
} else throw e;
} Prevention
- Refresh the connection list before disconnecting instead of using cached ids.
- Ensure all server instances share the same storage backend.
- Clean up client references when connections are removed from storage.
When it happens
Trigger: POST/DELETE disconnect route (tool-providers.ts:612) where the storage lookup finds no row for connectionId and the caller lacks admin bypass (hasAdminBypass for TOOL_PROVIDERS_RESOURCE).
Common situations: Connection row deleted from storage while still existing provider-side (stale client cache); storage not shared between the server handling the request and where the connection was created; wrong connectionId (typo or id from another environment); non-admin user trying to revoke someone else's connection.
Related errors
- You do not have permission to view usage for this connection
- Access denied: durable run belongs to a different resource
- Access denied: cannot save messages for a different resource
- Access denied: unable to verify message ownership
- Cannot authorize caller-supplied connection: request context
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/67fa376c2f548ea7.
Report an issue: GitHub.