cube-js/cube · error

A user-defined contextToApiScopes function returns an…

Error message

A user-defined contextToApiScopes function returns an inconsistent type.

What it means

The user-supplied `contextToApiScopes` option must resolve to an array of API scope strings. If it returns null, undefined, or a non-array value (a string, object, Promise resolving to something else), the gateway throws this plain Error while computing the request's API scopes.

Solutions

  1. Always return an array from contextToApiScopes, e.g. `return ['data']` instead of `return 'data'`.
  2. Ensure every code branch in the function returns a value (add a default branch returning defaultApiScopes).
  3. Await any async lookups (roles DB queries) before returning so the function doesn't return a pending promise mishandled as falsy.
  4. Validate the return shape at the end: throw your own descriptive error if the computed scopes aren't an array.

Example fix

// before
contextToApiScopes: (ctx) => ctx.role === 'admin' ? ['data','meta','sql'] : 'data',
// after
contextToApiScopes: async (ctx, defaultApiScopes) =>
  ctx.role === 'admin' ? ['data', 'meta', 'sql'] : [...defaultApiScopes],
Defensive patterns

Strategy: validation

Validate before calling

async function safeContextToApiScopes(ctx, defaultApiScopes) {
  const scopes = await myContextToApiScopes(ctx, defaultApiScopes);
  if (!Array.isArray(scopes)) throw new TypeError('contextToApiScopes must return an array');
  return scopes;
}

Type guard

function isApiScopesArray(v) {
  return Array.isArray(v) && v.every(s => typeof s === 'string');
}

Try / catch

try {
  const scopes = await contextToApiScopes(ctx, defaults);
} catch (e) {
  if (/inconsistent type/.test(e.message)) {
    // fall back to defaults and alert: contextToApiScopes returned non-array
  }
  throw e;
}

Prevention

When it happens

Trigger: A custom contextToApiScopes(securityContext, defaultApiScopes) implementation returning a single string like 'data', returning undefined on a code path (e.g. missing role branch), or forgetting `await`/`return` inside the async function.

Common situations: Copying an example but short-circuiting with a conditional that falls through; refactoring the function to return a Set or object instead of an array; returning defaultApiScopes directly when it happens to be a falsy/unsupported value.

Related errors


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

Appendix: source

Thrown at packages/cubejs-api-gateway/src/gateway.ts:2782

      return {
        securityContext: ctx.securityContext
      };
    };
  }

  protected createContextToApiScopesFn(
    options: ApiGatewayOptions,
  ): ContextToApiScopesFn {
    return options.contextToApiScopes
      ? async (securityContext?: any, defaultApiScopes?: ApiScopes[]) => {
        const scopes = options.contextToApiScopes &&
            await options.contextToApiScopes(
              securityContext,
              defaultApiScopes,
            );
        if (!scopes || !Array.isArray(scopes)) {
          throw new Error(
            'A user-defined contextToApiScopes function returns an inconsistent type.'
          );
        } else {
          scopes.forEach((p) => {
            if (['graphql', 'meta', 'data', 'sql', 'jobs'].indexOf(p) === -1) {
              throw new Error(
                `A user-defined contextToApiScopes function returns a wrong scope: ${p}`
              );
            }
          });
        }
        return scopes;
      }
      : async () => {
        const defaultApiScope = getEnv('defaultApiScope');
        if (defaultApiScope) {
          return defaultApiScope;
        } else {

View on GitHub (pinned to 7d981676b3)