ToolJet/ToolJet · error · Error
HubSpot OAuth "scope" config is missing
Error message
HubSpot OAuth "scope" config is missing
What it means
HubSpot plugin's authUrl() throws this plain Error when getOAuthCredentials() returns falsy scopes. Unlike clientId/clientSecret (which can come from env), scopes are config-only — they must be present in source_options. This is the third guard in the authUrl pre-flight sequence and runs after clientId and clientSecret checks pass.
Source
Thrown at marketplace/plugins/hubspot/lib/index.ts:122
authUrl(source_options: SourceOptions): string {
const { clientId, clientSecret, scopes, redirectUri } = this.getOAuthCredentials(source_options);
const oauth_type = source_options.oauth_type.value;
if (!clientId) {
throw new Error(
`HubSpot OAuth "clientId" ${oauth_type === 'tooljet_app' ? 'environment variable' : 'config'} is missing`
);
}
if (!clientSecret) {
throw new Error(
`HubSpot OAuth "clientSecret" ${oauth_type === 'tooljet_app' ? 'environment variable' : 'config'} is missing`
);
}
if (!scopes) {
throw new Error(`HubSpot OAuth "scope" config is missing`);
}
const baseUrl = 'https://app.hubspot.com/oauth/authorize';
const params = new URLSearchParams({
response_type: 'code',
client_id: clientId,
redirect_uri: redirectUri,
scope: scopes,
});
const authUrl = `${baseUrl}?${params.toString()}`;
return authUrl;
}
async accessDetailsFrom(authCode: string, source_options: any, resetSecureData = false): Promise<object> {
if (resetSecureData) {
return [View on GitHub (pinned to 20602a8e10)
Solutions
- Open the datasource config and enter at least one HubSpot OAuth scope, e.g. 'contacts' (space-separated for multiple: 'contacts companies deals').
- Confirm the value matches the shape getOAuthCredentials expects (string truthy). If it expects a string and you supplied an array, serialize it.
- Match the scopes against what is registered on the HubSpot app (HubSpot will reject scopes not declared on the app).
- Re-trigger authUrl() after saving to confirm the redirect URL builds.
Example fix
// before — datasource config with empty scopes
source_options = {
oauth_type: { value: 'custom' },
client_id: '...',
client_secret: '...',
scopes: '' // or undefined
};
// after
source_options = {
oauth_type: { value: 'custom' },
client_id: '...',
client_secret: '...',
scopes: 'contacts companies deals'
}; Defensive patterns
Strategy: validation
Validate before calling
function ensureHubSpotScopes(source_options) {
const scopes = source_options?.scopes?.value ?? source_options?.scopes;
if (!scopes || (typeof scopes === 'string' && scopes.trim() === '')) {
throw new Error('HubSpot OAuth scopes are required (e.g. "contacts companies")');
}
return scopes;
}
ensureHubSpotScopes(source_options); Type guard
function hasHubSpotScopes(source_options): boolean {
const s = source_options?.scopes?.value ?? source_options?.scopes;
return typeof s === 'string' && s.trim().length > 0;
} Try / catch
try {
const url = plugin.authUrl(source_options);
} catch (e) {
if (/scope.*missing/.test(e.message)) {
return { action: 'configure_scopes' };
}
throw e;
} Prevention
- Make scopes a required field in the datasource UI when clientId/clientSecret are filled.
- Only request scopes registered on the HubSpot app — otherwise HubSpot rejects the consent.
- Document scope strings as space-separated in the field's help text.
When it happens
Trigger: Datasource config has clientId and clientSecret filled but the scopes field is empty or undefined. Happens when the form field is optional in the UI but the OAuth flow requires at least one scope (HubSpot requires explicit scopes; empty scope is rejected).
Common situations: New datasource setup where the operator skipped the scopes input; migration that preserved credentials but dropped scopes; scopes were set as an array but getOAuthCredentials expected a space- or comma-delimited string (or vice versa) and returned falsy.
Related errors
- HubSpot OAuth "clientId" ${oauth_type === 'tooljet_app' ? 'e
- HubSpot OAuth "clientSecret" ${oauth_type === 'tooljet_app'
- Google OAuth "clientId" ${oauth_type === 'tooljet_app' ? 'en
- Scope is required
- Asana OAuth client_id is missing
AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13).
Data as JSON: /api/errors/ab64a02dddded95f.
Report an issue: GitHub.