{"record":{"id":"1361b5e2f4bf94ec","repo":"mastra-ai/mastra","slug":"cannot-authorize-caller-supplied-connection-reque","errorCode":null,"errorMessage":"Cannot authorize caller-supplied connection: request context has no '${MASTRA_RESOURCE_ID_KEY}'. Set requestContext.set('${MASTRA_RESOURCE_ID_KEY}', <userId>) before calling /authorize.","messagePattern":"Cannot authorize caller-supplied connection: request context has no '(.+?)'\\. Set requestContext\\.set\\('(.+?)', <userId>\\) before calling /authorize\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"packages/server/src/server/handlers/tool-providers.ts","lineNumber":294,"sourceCode":"        throw new HTTPException(400, { message: `Tool provider ${providerId} does not support authorize` });\n      }\n      // Per-pin scope:\n      // - 'shared' buckets under SHARED_BUCKET_ID.\n      // - 'caller-supplied' buckets under request-context resourceId (400 if missing).\n      // - 'per-author' (default) buckets under the caller's resolved authorId.\n      //\n      // Precedence: an explicit request `scope` wins, then the provider's\n      // config-level `defaultScope` (the app author's tenancy decision), then\n      // `'per-author'`. This lets a provider constructed with\n      // `defaultScope: 'caller-supplied'` produce per-tenant connections even\n      // though no UI control selects a scope.\n      const requestedScope = scope ?? provider.defaultScope;\n      const effectiveScope: 'shared' | 'per-author' | 'caller-supplied' =\n        requestedScope === 'shared' || requestedScope === 'caller-supplied' ? requestedScope : 'per-author';\n      const callerResourceId = requestContext?.get(MASTRA_RESOURCE_ID_KEY);\n      if (effectiveScope === 'caller-supplied') {\n        if (typeof callerResourceId !== 'string' || callerResourceId.length === 0) {\n          throw new HTTPException(400, {\n            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.`,\n          });\n        }\n      }\n      const callerAuthorId = resolveOwnerId(requestContext, mastra.getLogger());\n      const ownerAuthorId =\n        effectiveScope === 'shared'\n          ? SHARED_BUCKET_ID\n          : effectiveScope === 'caller-supplied'\n            ? (callerResourceId as string)\n            : callerAuthorId;\n\n      // Fresh connect (no connectionId) uses the resolved owner id as the\n      // provider bucket so the adapter creates the connection under the same\n      // userId the runtime will resolve to at execution time. Re-auth (caller\n      // passed an existing connectionId) is left untouched.\n      const bucket = connectionId && connectionId.length > 0 ? connectionId : ownerAuthorId;\n      const result = await provider.authorize({ toolkit, connectionId: bucket, toolName, config });","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/tool-providers.ts#L276-L312","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nawait client.authorize({ providerId: 'acme', scope: 'caller-supplied' });\n// after\nrequestContext.set(MASTRA_RESOURCE_ID_KEY, currentUser.id);\nawait client.authorize({ providerId: 'acme', scope: 'caller-supplied', requestContext });","handlingStrategy":"validation","validationCode":"if (scope === 'caller-supplied') {\n  const rid = requestContext?.get(MASTRA_RESOURCE_ID_KEY);\n  if (typeof rid !== 'string' || rid.length === 0) {\n    throw new Error(`Set requestContext.set('${MASTRA_RESOURCE_ID_KEY}', userId) before caller-supplied authorize`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await api.authorize({ providerId, scope: 'caller-supplied', requestContext });\n} catch (e) {\n  if (e.status === 400 && e.message.includes(MASTRA_RESOURCE_ID_KEY)) {\n    // prompt user to sign in / supply userId, then retry\n  } else throw e;\n}","preventionTips":["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."],"tags":["http-400","tool-providers","request-context","authorization","multi-tenancy"],"backgroundTag":"missing-request-context-value","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}