usebruno/bruno · error · Error

Client ID is required for OAuth2 client credentials flow

Error message

Client ID is required for OAuth2 client credentials flow

What it means

Thrown by fetchTokenClientCredentials when oauth2Config.clientId is falsy. The client_credentials grant authenticates the application itself (not a user), so a client_id is mandatory to identify the app to the authorization server. Without it the token request is malformed.

Source

Thrown at packages/bruno-requests/src/auth/oauth2-helper.ts:126

/**
 * Fetches an OAuth2 token using client credentials grant
 */
const fetchTokenClientCredentials = async (oauth2Config: OAuth2Config, axiosInstance?: AxiosInstance) => {
  const {
    accessTokenUrl,
    clientId,
    clientSecret,
    scope,
    credentialsPlacement = 'basic_auth_header',
    additionalParameters
  } = oauth2Config;

  if (!accessTokenUrl) {
    throw new Error('Access Token URL is required for OAuth2 client credentials flow');
  }

  if (!clientId) {
    throw new Error('Client ID is required for OAuth2 client credentials flow');
  }

  const requestConfig: RequestConfig = {
    method: 'POST',
    url: accessTokenUrl,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json'
    },
    data: '',
    responseType: 'arraybuffer'
  };

  const data: ClientCredentialsData = {
    grant_type: 'client_credentials'
  };

  if (scope && scope.trim() !== '') {

View on GitHub (pinned to 9bdd81c7bd)

Solutions

  1. Provide a non-empty clientId in the OAuth2Config for client_credentials requests.
  2. Confirm the env var / secret store actually returns the client id (check for trailing whitespace or undefined).
  3. Gate the call with a guard that fails fast with a clearer message than the helper's.

Example fix

// before
const config = { grantType: 'client_credentials', accessTokenUrl: tokenUrl, clientSecret: secret };

// after
const config = {
  grantType: 'client_credentials',
  accessTokenUrl: tokenUrl,
  clientId: process.env.OAUTH_CLIENT_ID,
  clientSecret: secret
};
Defensive patterns

Strategy: validation

Validate before calling

if (!config.clientId) throw new Error('OAUTH_CLIENT_ID env var is not set');
await getOAuth2AccessToken(config, tokenStore);

Type guard

function hasClientId(c) { return typeof c.clientId === 'string' && c.clientId.trim().length > 0; }

Try / catch

try { await getOAuth2AccessToken(config, tokenStore); }
catch (e) { if (e.message === 'Client ID is required for OAuth2 client credentials flow') { /* load secret */ } else throw e; }

Prevention

When it happens

Trigger: getOAuth2AccessToken called with grantType='client_credentials' and a valid accessTokenUrl, but clientId omitted, empty, or undefined. Common when secrets are sourced from env vars that are not set in the current environment.

Common situations: CI/CD pipeline forgot to inject OAUTH_CLIENT_ID; the clientId was stored only in the Bruno GUI collection but not serialized into the programmatic config; a refactor renamed the field.

Related errors


AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13). Data as JSON: /api/errors/91de575850beb3f7. Report an issue: GitHub.