jlcodes99/cockpit-tools · error

PROVIDER_CREDENTIAL_INVALID

PROVIDER_CREDENTIAL_INVALID

Error message

PROVIDER_CREDENTIAL_INVALID

What it means

Thrown when upserting a provider from a credential pair and either the normalized base URL or the sanitized API key is empty/invalid. The service requires both a valid Codex-compatible base URL and a non-empty key before it will locate or create a provider. It fires before any store lookup, so nothing is persisted.

Source

Thrown at src/services/codexModelProviderService.ts:949

): Promise<CodexModelProvider> {
  return updateCodexModelProvider(providerId, { integrationType });
}

export async function deleteCodexModelProvider(providerId: string): Promise<void> {
  const providers = await ensureProvidersLoaded();
  const next = providers.filter((item) => item.id !== providerId);
  if (next.length === providers.length) return;
  await writeProviders(next);
}

export async function upsertCodexModelProviderFromCredential(
  input: UpsertFromCredentialInput,
): Promise<CodexModelProvider> {
  const apiBaseUrl = normalizeBaseUrlForStore(input.apiBaseUrl);
  const normalizedBaseUrl = normalizeCodexModelProviderBaseUrl(apiBaseUrl);
  const apiKey = sanitizeApiKey(input.apiKey);
  if (!normalizedBaseUrl || !apiKey) {
    throw new Error('PROVIDER_CREDENTIAL_INVALID');
  }
  const providers = await ensureProvidersLoaded();
  let provider = findCodexModelProviderById(providers, input.providerId);
  if (!provider) {
    provider = findCodexModelProviderByBaseUrl(providers, apiBaseUrl);
  }

  if (!provider) {
    const now = Date.now();
    const wireApi = normalizeWireApi(input.wireApi);
    provider = {
      id: createProviderId(),
      name:
        sanitizeName(input.providerName ?? '') ||
        deriveProviderNameFromBaseUrl(apiBaseUrl),
      baseUrl: apiBaseUrl,
      sourceTag: sanitizeName(input.sourceTag ?? '') || undefined,
      modelCatalog:

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Ensure apiBaseUrl is a full, valid URL the Codex normalizer accepts (e.g. https://host).
  2. Ensure apiKey is a non-empty real secret with no surrounding whitespace or placeholder text.
  3. Log/check the values after sanitizeApiKey/normalizeCodexModelProviderBaseUrl to see which one is empty before calling the API.

Example fix

// before
await upsertProviderFromCredential({ apiBaseUrl: 'localhost:8080', apiKey: '' });
// after
const apiBaseUrl = 'https://localhost:8080';
const apiKey = process.env.MY_API_KEY?.trim();
if (!apiKey) throw new Error('set MY_API_KEY before syncing the provider');
await upsertProviderFromCredential({ apiBaseUrl, apiKey });
Defensive patterns

Strategy: validation

Validate before calling

function isCredentialValid(apiBaseUrl, apiKey) {
  let url;
  try { url = new URL(apiBaseUrl); } catch { return false; }
  return (url.protocol === 'https:' || url.protocol === 'http:')
    && typeof apiKey === 'string' && apiKey.trim().length > 0;
}

Try / catch

try {
  await upsertProviderFromCredential(input);
} catch (e) {
  if (e.message === 'PROVIDER_CREDENTIAL_INVALID') {
    console.error('Check apiBaseUrl scheme/host and that apiKey is a non-empty trimmed secret.');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the credential upsert with input.apiBaseUrl that fails normalizeCodexModelProviderBaseUrl (missing scheme, unsupported host); input.apiKey empty or rejected by sanitizeApiKey (e.g. whitespace/placeholder); both fields blank in a form submit.

Common situations: User pasted a base URL without https://; trailing garbage or wrong host for a Codex endpoint; API key field left blank or containing 'YOUR_KEY' placeholder; env/config interpolation produced an empty string.

Related errors


AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05). Data as JSON: /api/errors/6d08049c28d405e6. Report an issue: GitHub.