cube-js/cube · error · CubejsHandlerError
API scope is missing: ${scope}
Error message
API scope is missing: ${scope} What it means
Cube enforces API scopes per request: assertApiScope computes the scopes for the request's securityContext (via contextToApiScopes or the default) and throws this 403 Forbidden if the scope required by the endpoint (e.g. 'sql', 'jobs', 'graphql') is not among them. Authentication succeeded; the token's identity simply lacks permission for this API surface.
Source
Thrown at packages/cubejs-api-gateway/src/gateway.ts:2817
return defaultApiScope;
} else {
return this.contextToApiScopesDefFn();
}
};
}
protected async assertApiScope(
scope: ApiScopes,
securityContext?: any,
): Promise<void> {
const scopes =
await this.contextToApiScopesFn(
securityContext || {},
getEnv('defaultApiScope') || await this.contextToApiScopesDefFn(),
);
const permited = scopes.indexOf(scope) >= 0;
if (!permited) {
throw new CubejsHandlerError(
403,
'Forbidden',
`API scope is missing: ${scope}`
);
}
}
protected extractAuthorizationHeaderWithSchema(req: Request) {
const authHeader = req.headers?.['x-cube-authorization'] || req.headers?.authorization;
if (typeof authHeader === 'string') {
const parts = authHeader.split(' ', 2);
if (parts.length === 1) {
return parts[0];
}
return parts[1];
}View on GitHub (pinned to 7d981676b3)
Solutions
- Update contextToApiScopes to grant the missing scope for the relevant securityContexts (add 'sql'/'jobs'/etc. for roles that need it).
- Check the defaultApiScope environment variable — if it excludes the scope, either widen it or ensure tokens carry claims mapping to broader scopes.
- Verify the request's token actually contains the claims your contextToApiScopes inspects (decode the JWT).
- If the client shouldn't use that endpoint, stop calling it (e.g. use the REST data API instead of the SQL API).
Example fix
// before contextToApiScopes: (ctx) => ['data'], // after contextToApiScopes: (ctx) => ctx.role === 'analyst' ? ['data', 'sql'] : ['data'],
Defensive patterns
Strategy: validation
Validate before calling
async function assertScopeAllowed(securityContext, requiredScope) {
const scopes = await contextToApiScopes(securityContext, ['data']);
if (!scopes.includes(requiredScope)) {
throw new Error(`UI should hide this endpoint: missing scope ${requiredScope}`);
}
} Type guard
function hasScope(scopes, scope) {
return Array.isArray(scopes) && scopes.includes(scope);
} Try / catch
try {
const result = await cubeApi.sql(); // or jobs/graphql endpoint
} catch (e) {
if (e.status === 403 && /API scope is missing/.test(e.message)) {
// token identity lacks this scope; route user to a permitted surface or upgrade their role
}
throw e;
} Prevention
- Hide gated endpoints (SQL API, jobs) in your app when the user's scopes don't include them
- Keep contextToApiScopes logic aligned with role management changes
- Write integration tests per role asserting which endpoints succeed
- Check the defaultApiScope env var after environment migrations
When it happens
Trigger: A request to a scope-gated endpoint (SQL API, jobs API, GraphQL) whose securityContext resolves to scopes that don't include the required scope — e.g. contextToApiScopes returns ['data'] but the client hits /v1/sql or the SQL interface.
Common situations: After tightening contextToApiScopes or setting the defaultApiScope env var too narrowly, previously working clients start failing; users with viewer roles attempting to use Cube SQL; tokens without role claims falling into the most restrictive branch.
Related errors
- JWT without kid inside headers
- Unable to verify, JWK with kid: "${decoded.header.kid}" not
- Invalid token
- Authorization header isn't set
- Unable to decode JWT key
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/a3b6712eb545d1bb.
Report an issue: GitHub.