decolua/9router · error

clientId is required for external_idp refresh

Error message

clientId is required for external_idp refresh

What it means

When building refresh parameters, the clientId must be present in providerSpecificData (as clientId or client_id). It is sent as the client_id field of the Microsoft token refresh POST. This guard fires when the stored provider-specific metadata lacks the client id — usually meaning the credential was created by a path that skipped normalization.

Source

Thrown at src/lib/oauth/kiroExternalIdp.js:136

    providerSpecificData: {
      profileArn,
      region,
      authMethod: "external_idp",
      provider: "CLIProxyAPI",
      clientId,
      tokenEndpoint,
      scope,
    },
  };
}

export function buildExternalIdpRefreshParams(refreshToken, providerSpecificData = {}) {
  const clientId = normalizeString(providerSpecificData.clientId || providerSpecificData.client_id);
  const tokenEndpoint = validateMicrosoftTokenEndpoint(providerSpecificData.tokenEndpoint || providerSpecificData.token_endpoint);
  const scope = normalizeScope(providerSpecificData.scope || providerSpecificData.scopes);

  if (!refreshToken) throw new Error("refresh token is required");
  if (!clientId) throw new Error("clientId is required for external_idp refresh");
  if (!scope) throw new Error("scope is required for external_idp refresh");

  return {
    tokenEndpoint,
    body: new URLSearchParams({
      grant_type: "refresh_token",
      client_id: clientId,
      refresh_token: refreshToken,
      scope,
    }),
    providerSpecificData: {
      ...providerSpecificData,
      authMethod: "external_idp",
      clientId,
      tokenEndpoint,
      scope,
    },
  };

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Ensure accounts are created through normalizeKiroExternalIdpAuth so providerSpecificData.clientId is populated
  2. Re-import the account with the complete CLIProxyAPI auth JSON including client_id
  3. If migrating, backfill providerSpecificData.clientId from the original auth file before refreshing
  4. Check key names: only clientId and client_id are read from providerSpecificData

Example fix

// before
buildExternalIdpRefreshParams(rt, { token_endpoint: url, scopes: 'openid' })
// after
buildExternalIdpRefreshParams(rt, { token_endpoint: url, scopes: 'openid', client_id: '04b07795-8ddb-461a-bbee-02f9e1bf7b46' })
Defensive patterns

Strategy: validation

Validate before calling

const psd = account.providerSpecificData ?? {};
const clientId = psd.clientId ?? psd.client_id;
if (typeof clientId !== 'string' || !clientId.trim()) {
  throw new Error('providerSpecificData missing clientId; re-import the account');
}

Type guard

function hasRefreshClientId(psd) {
  return typeof psd === 'object' && psd !== null &&
    ['clientId', 'client_id'].some(k => typeof psd[k] === 'string' && psd[k].trim() !== '');
}

Try / catch

try {
  const params = buildExternalIdpRefreshParams(rt, account.providerSpecificData);
} catch (e) {
  if (e.message === 'clientId is required for external_idp refresh') {
    console.error('Account metadata incomplete; re-import with full CLIProxyAPI auth JSON');
  }
  throw e;
}

Prevention

When it happens

Trigger: buildExternalIdpRefreshParams(refreshToken, {}) or with metadata missing clientId/client_id, e.g. data constructed manually, migrated from an older schema, or with keys named 'clientID'/'application_id' that aren't recognized.

Common situations: Accounts migrated from an older storage layout that stored client metadata elsewhere; hand-crafted providerSpecificData in tests or scripts; a partial object spread that dropped clientId; importing via a path that never ran normalizeKiroExternalIdpAuth.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/4bdc462df3daa15b. Report an issue: GitHub.