cube-js/cube · error · CubejsHandlerError
Invalid token
Error message
Invalid token
What it means
This is the generic wrapper: when the configured checkAuth function (default JWT verification or a user-supplied `checkAuth`) throws for a presented token and the gateway runs with security checks enforced (production mode, no dev/prod toggle disabling them), Cube rethrows it as a 403 'Invalid token'. The original error is attached as `cause`, so the underlying reason (bad signature, expiry, missing kid, etc.) is inside it.
Source
Thrown at packages/cubejs-api-gateway/src/gateway.ts:2710
403,
'Forbidden',
`Unable to verify, JWK with kid: "${decoded.header.kid}" not found`
);
}
return verifyToken(auth, jwk);
};
}
return async (req, auth) => {
if (auth) {
try {
req.securityContext = await checkAuthFn(auth);
req.signedWithPlaygroundAuthSecret =
Boolean(internalOptions?.isPlaygroundCheckAuth) && hasDevTokenScope(req.securityContext);
} catch (e: any) {
if (this.enforceSecurityChecks) {
throw new CubejsHandlerError(403, 'Forbidden', 'Invalid token', e);
}
}
} else if (this.enforceSecurityChecks) {
// @todo Move it to 401 or 400
throw new CubejsHandlerError(403, 'Forbidden', 'Authorization header isn\'t set');
}
return {
securityContext: req.securityContext
};
};
}
protected createCheckAuthFn(options: ApiGatewayOptions): PreparedCheckAuthFn {
const mainCheckAuthFn = options.checkAuth
? this.wrapCheckAuth(options.checkAuth)
: this.createDefaultCheckAuth(options.jwt);
View on GitHub (pinned to 7d981676b3)
Solutions
- Inspect the error's `cause` to get the real reason (e.g. 'jwt expired', 'invalid signature') and fix that underlying issue.
- Re-generate the token with the correct current signing secret and algorithm configured for the Cube instance.
- Verify client clock and token `exp`/`nbf` claims; issue tokens with a reasonable validity window.
- If using a custom checkAuth, add logging/fix its exception path so valid tokens don't throw.
- For local development only, run with dev mode enabled where enforceSecurityChecks is false (never in production).
Example fix
// before
const token = jwt.sign(payload, OLD_SECRET, { expiresIn: '1s' });
// after
const token = jwt.sign(payload, process.env.CUBEJS_API_SECRET, { expiresIn: '1h' }); Defensive patterns
Strategy: try-catch
Validate before calling
function tokenLooksValid(token, secret) {
try { jwt.verify(token, secret); return true; } catch { return false; }
}
if (!tokenLooksValid(myToken, process.env.CUBEJS_API_SECRET)) {
myToken = refreshToken(); // before calling Cube
} Type guard
function isCubeForbiddenError(e) {
return !!e && typeof e.status === 'number' && e.status === 403 && typeof e.message === 'string';
} Try / catch
try {
const rs = await cubeApi.load(query);
} catch (e) {
if (isCubeForbiddenError(e) && e.message === 'Invalid token') {
const reason = e.cause?.message; // real cause: 'jwt expired', 'invalid signature', etc.
await refreshSession();
return cubeApi.load(query); // single retry after token refresh
}
throw e;
} Prevention
- Log e.cause on 403s to surface the real verification failure
- Use short-lived tokens with an automatic refresh flow in clients
- Keep signing secrets in sync across environments via one source (env/secret manager)
- Monitor clock skew between token issuer and Cube server (NTP)
When it happens
Trigger: Any Authorization header whose token fails the checkAuth function — expired JWT, wrong signing key, malformed token, custom checkAuth throwing — while `enforceSecurityChecks` is true (i.e. not running with CUBEJS_DEV_MODE-style disabled checks).
Common situations: Expired tokens after session timeout; mismatched JWT secret after rotating CUBEJS_API_SECRET or moving environments; clock skew between token issuer and Cube server; custom checkAuth rejecting valid users due to a bug; forgetting that in dev mode the same token succeeds but production enforces checks.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- JWT without kid inside headers
- Unable to verify, JWK with kid: "${decoded.header.kid}" not
- Unable to decode JWT key
- Authorization header isn't set
- API scope is missing: ${scope}
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/bb59842557400fc3.
Report an issue: GitHub.