{"record":{"id":"6d08049c28d405e6","repo":"jlcodes99/cockpit-tools","slug":"provider-credential-invalid","errorCode":"PROVIDER_CREDENTIAL_INVALID","errorMessage":"PROVIDER_CREDENTIAL_INVALID","messagePattern":"PROVIDER_CREDENTIAL_INVALID","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/codexModelProviderService.ts","lineNumber":949,"sourceCode":"): Promise<CodexModelProvider> {\n  return updateCodexModelProvider(providerId, { integrationType });\n}\n\nexport async function deleteCodexModelProvider(providerId: string): Promise<void> {\n  const providers = await ensureProvidersLoaded();\n  const next = providers.filter((item) => item.id !== providerId);\n  if (next.length === providers.length) return;\n  await writeProviders(next);\n}\n\nexport async function upsertCodexModelProviderFromCredential(\n  input: UpsertFromCredentialInput,\n): Promise<CodexModelProvider> {\n  const apiBaseUrl = normalizeBaseUrlForStore(input.apiBaseUrl);\n  const normalizedBaseUrl = normalizeCodexModelProviderBaseUrl(apiBaseUrl);\n  const apiKey = sanitizeApiKey(input.apiKey);\n  if (!normalizedBaseUrl || !apiKey) {\n    throw new Error('PROVIDER_CREDENTIAL_INVALID');\n  }\n  const providers = await ensureProvidersLoaded();\n  let provider = findCodexModelProviderById(providers, input.providerId);\n  if (!provider) {\n    provider = findCodexModelProviderByBaseUrl(providers, apiBaseUrl);\n  }\n\n  if (!provider) {\n    const now = Date.now();\n    const wireApi = normalizeWireApi(input.wireApi);\n    provider = {\n      id: createProviderId(),\n      name:\n        sanitizeName(input.providerName ?? '') ||\n        deriveProviderNameFromBaseUrl(apiBaseUrl),\n      baseUrl: apiBaseUrl,\n      sourceTag: sanitizeName(input.sourceTag ?? '') || undefined,\n      modelCatalog:","sourceCodeStart":931,"sourceCodeEnd":967,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/services/codexModelProviderService.ts#L931-L967","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure apiBaseUrl is a full, valid URL the Codex normalizer accepts (e.g. https://host).","Ensure apiKey is a non-empty real secret with no surrounding whitespace or placeholder text.","Log/check the values after sanitizeApiKey/normalizeCodexModelProviderBaseUrl to see which one is empty before calling the API."],"exampleFix":"// before\nawait upsertProviderFromCredential({ apiBaseUrl: 'localhost:8080', apiKey: '' });\n// after\nconst apiBaseUrl = 'https://localhost:8080';\nconst apiKey = process.env.MY_API_KEY?.trim();\nif (!apiKey) throw new Error('set MY_API_KEY before syncing the provider');\nawait upsertProviderFromCredential({ apiBaseUrl, apiKey });","handlingStrategy":"validation","validationCode":"function isCredentialValid(apiBaseUrl, apiKey) {\n  let url;\n  try { url = new URL(apiBaseUrl); } catch { return false; }\n  return (url.protocol === 'https:' || url.protocol === 'http:')\n    && typeof apiKey === 'string' && apiKey.trim().length > 0;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await upsertProviderFromCredential(input);\n} catch (e) {\n  if (e.message === 'PROVIDER_CREDENTIAL_INVALID') {\n    console.error('Check apiBaseUrl scheme/host and that apiKey is a non-empty trimmed secret.');\n  } else throw e;\n}","preventionTips":["Validate the URL with new URL() and require http(s) before submitting.","Never pass placeholder strings like 'YOUR_API_KEY'; check env vars resolved to non-empty values.","Trim user input on both fields before calling the API."],"tags":["validation","provider","credentials","base-url"],"backgroundTag":"invalid-credentials","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}