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

  1. Set the resource id before calling authorize: requestContext.set(MASTRA_RESOURCE_ID_KEY, userId).
  2. Use scope 'per-author' (default) or 'shared' if you don't need caller-supplied bucketing.
  3. Add middleware that resolves the authenticated user into the request context for authorize routes.
  4. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/1361b5e2f4bf94ec. Report an issue: GitHub.