cube-js/cube · error

You cannot change security context via __user from

Error message

You cannot change security context via __user from ${session.user} to ${request.meta.changeUser}, because it's not allowed.

What it means

When a SQL client sends the special __user meta property (session variable in the SQL query) with a different username, Cube tries to switch the security context to that user. Switching is only allowed for superusers or when the canSwitchSqlUser callback permits it; otherwise the SQL server throws this error naming the originating and requested users.

Solutions

  1. Configure canSwitchSqlUser in SQLServer options to authorize the specific user switch.
  2. Connect with the SQL superuser account (sqlSuperUser) when impersonation via __user is required.
  3. Remove or correct the __user variable in the client's session settings/BI tool configuration so it matches the authenticated user.

Example fix

// before
const server = new SQLServer(apiGateway, { gatewayPort });
// after
const server = new SQLServer(apiGateway, {
  gatewayPort,
  canSwitchSqlUser: async (fromUser, toUser) => toUser.startsWith('tenant_')
});
Defensive patterns

Strategy: try-catch

Validate before calling

const mayImpersonate = async (session, targetUser) =>
  session.superuser || (typeof canSwitchSqlUser === 'function' && await canSwitchSqlUser(session.user, targetUser));
// check mayImpersonate(session, request.meta.changeUser) before sending __user

Type guard

function requestsImpersonation(msg: { meta?: { changeUser?: string, protocol?: string } }, sessionUser: string): boolean {
  return !!msg.meta?.changeUser && msg.meta.changeUser !== sessionUser;
}

Try / catch

try {
  await runSqlWithImpersonation(query, targetUser);
} catch (e) {
  if (e.message.startsWith("You cannot change security context via __user")) {
    console.error(`Impersonation from ${session.user} to ${targetUser} denied; connect as superuser or configure canSwitchSqlUser`);
  } else throw e;
}

Prevention

When it happens

Trigger: A SQL client issues a query whose __user session variable differs from the authenticated SQL user (e.g. `SET __user = 'other'; SELECT ...`) while the authenticated session is not a superuser and no canSwitchSqlUser option authorizes the switch.

Common situations: BI tools configured with a tenant/user template variable that injects __user while connecting as a shared SQL account; developers imitating multi-tenant impersonation without defining canSwitchSqlUser; connecting as a non-superuser account (sqlUser) instead of the superuser (sqlSuperUser) when impersonation is intended.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/36819c073da6c079. Report an issue: GitHub.

Appendix: source

Thrown at packages/cubejs-api-gateway/src/sql-server.ts:121

    const canSwitchSqlUser: CanSwitchSQLUserFn = options.canSwitchSqlUser
      || this.createDefaultCanSwitchSqlUserFn(options);

    const contextByRequest = async (request, session) => {
      let userForContext = session.user;
      let { securityContext } = session;

      if (request.meta.changeUser && request.meta.changeUser !== session.user) {
        const sqlAuthRequest: SqlAuthServiceAuthenticateRequest = {
          protocol: request.meta.protocol,
          method: 'password',
        };
        const canSwitch = session.superuser || await canSwitchSqlUser(session.user, request.meta.changeUser);
        if (canSwitch) {
          userForContext = request.meta.changeUser;
          const current = await checkSqlAuth({ ...request, ...sqlAuthRequest }, userForContext, null);
          securityContext = current.securityContext;
        } else {
          throw new Error(
            `You cannot change security context via __user from ${session.user} to ${request.meta.changeUser}, because it's not allowed.`
          );
        }
      }
      return this.contextByNativeReq(request, securityContext, request.id);
    };

    const canSwitchUserForSession = async (session, user) => session.superuser || canSwitchSqlUser(session.user, user);

    this.sqlInterfaceInstance = await registerInterface({
      gatewayPort: this.gatewayPort,
      pgPort: options.pgSqlPort,
      contextToApiScopes: async ({ securityContext }) => this.apiGateway.contextToApiScopesFn(
        securityContext,
        getEnv('defaultApiScope') || await this.apiGateway.contextToApiScopesDefFn()
      ),
      checkAuth: async ({ request, token }) => {
        const { securityContext } = await this.apiGateway.checkAuthFn(request, token);

View on GitHub (pinned to 7d981676b3)