mastra-ai/mastra · error · HTTPException
Cannot authorize caller-supplied connection: request context
Error message
Cannot authorize caller-supplied connection: request context has no '${MASTRA_RESOURCE_ID_KEY}'. Set requestContext.set('${MASTRA_RESOURCE_ID_KEY}', <userId>) before calling /authorize. What it means
HTTP 400 thrown when scope='caller-supplied' is requested during /authorize but requestContext.get(MASTRA_RESOURCE_ID_KEY) is missing or empty. Caller-supplied scope buckets the connection under a resourceId the caller must supply explicitly, so the library fails closed instead of attributing the connection to an unknown user.
Source
Thrown at packages/server/src/server/handlers/tool-providers.ts:294
throw new HTTPException(400, { message: `Tool provider ${providerId} does not support authorize` });
}
// Per-pin scope:
// - 'shared' buckets under SHARED_BUCKET_ID.
// - 'caller-supplied' buckets under request-context resourceId (400 if missing).
// - 'per-author' (default) buckets under the caller's resolved authorId.
//
// Precedence: an explicit request `scope` wins, then the provider's
// config-level `defaultScope` (the app author's tenancy decision), then
// `'per-author'`. This lets a provider constructed with
// `defaultScope: 'caller-supplied'` produce per-tenant connections even
// though no UI control selects a scope.
const requestedScope = scope ?? provider.defaultScope;
const effectiveScope: 'shared' | 'per-author' | 'caller-supplied' =
requestedScope === 'shared' || requestedScope === 'caller-supplied' ? requestedScope : 'per-author';
const callerResourceId = requestContext?.get(MASTRA_RESOURCE_ID_KEY);
if (effectiveScope === 'caller-supplied') {
if (typeof callerResourceId !== 'string' || callerResourceId.length === 0) {
throw new HTTPException(400, {
message: `Cannot authorize caller-supplied connection: request context has no '${MASTRA_RESOURCE_ID_KEY}'. Set requestContext.set('${MASTRA_RESOURCE_ID_KEY}', <userId>) before calling /authorize.`,
});
}
}
const callerAuthorId = resolveOwnerId(requestContext, mastra.getLogger());
const ownerAuthorId =
effectiveScope === 'shared'
? SHARED_BUCKET_ID
: effectiveScope === 'caller-supplied'
? (callerResourceId as string)
: callerAuthorId;
// Fresh connect (no connectionId) uses the resolved owner id as the
// provider bucket so the adapter creates the connection under the same
// userId the runtime will resolve to at execution time. Re-auth (caller
// passed an existing connectionId) is left untouched.
const bucket = connectionId && connectionId.length > 0 ? connectionId : ownerAuthorId;
const result = await provider.authorize({ toolkit, connectionId: bucket, toolName, config });View on GitHub (pinned to 75dd419e61)
Solutions
- Set the resource id before calling authorize: requestContext.set(MASTRA_RESOURCE_ID_KEY, userId).
- Use scope 'per-author' (default) or 'shared' if you don't need caller-supplied bucketing.
- Add middleware that resolves the authenticated user into the request context for authorize routes.
- Reject the request client-side when the user id is unknown instead of hitting /authorize.
Example fix
// before
await client.authorize({ providerId: 'acme', scope: 'caller-supplied' });
// after
requestContext.set(MASTRA_RESOURCE_ID_KEY, currentUser.id);
await client.authorize({ providerId: 'acme', scope: 'caller-supplied', requestContext }); Defensive patterns
Strategy: validation
Validate before calling
if (scope === 'caller-supplied') {
const rid = requestContext?.get(MASTRA_RESOURCE_ID_KEY);
if (typeof rid !== 'string' || rid.length === 0) {
throw new Error(`Set requestContext.set('${MASTRA_RESOURCE_ID_KEY}', userId) before caller-supplied authorize`);
}
} Try / catch
try {
return await api.authorize({ providerId, scope: 'caller-supplied', requestContext });
} catch (e) {
if (e.status === 400 && e.message.includes(MASTRA_RESOURCE_ID_KEY)) {
// prompt user to sign in / supply userId, then retry
} else throw e;
} Prevention
- Add middleware that always resolves the authenticated user into the request context.
- Never call authorize with caller-supplied scope from anonymous/unauthenticated paths.
- Default to 'per-author' scope unless caller-supplied bucketing is a hard requirement.
When it happens
Trigger: POST /api/tool-providers/:providerId/authorize with scope='caller-supplied' (or defaultScope resolving to it) while no MASTRA_RESOURCE_ID_KEY was set on the request context (tool-providers.ts:294).
Common situations: Server-side SDK calls to authorize that skip populating the request context; middleware stripped or never set the resource id (no authenticated user resolved); frontend sends scope=caller-supplied but the hosting app does not forward the user id.
Related errors
- You do not have permission to disconnect this connection
- You do not have permission to view usage for this connection
- Session is not available to the current user
- Factory session ${session.sessionId} is not available to the
- AGENT_MEMORY_THREAD_RESOURCE_MISMATCH
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1361b5e2f4bf94ec.
Report an issue: GitHub.