{"record":{"id":"a41227771de70f57","repo":"jlcodes99/cockpit-tools","slug":"api-key-required","errorCode":"API_KEY_REQUIRED","errorMessage":"API_KEY_REQUIRED","messagePattern":"API_KEY_REQUIRED","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/codexModelProviderService.ts","lineNumber":815,"sourceCode":"  if (!apiKey) throw new Error('API_KEY_NOT_FOUND');\n\n  const now = Date.now();\n  apiKey.name = sanitizeName(name);\n  apiKey.updatedAt = now;\n  provider.updatedAt = now;\n  await writeProviders(providers);\n  return { ...provider, apiKeys: provider.apiKeys.map((item) => ({ ...item })) };\n}\n\n/** Replace the secret for an existing provider API key without changing its id. */\nexport async function updateApiKeyOnCodexModelProvider(\n  providerId: string,\n  apiKeyId: string,\n  apiKey: string,\n  name?: string,\n): Promise<CodexModelProvider> {\n  const normalizedApiKey = sanitizeApiKey(apiKey);\n  if (!normalizedApiKey) throw new Error(\"API_KEY_REQUIRED\");\n\n  const providers = await ensureProvidersLoaded();\n  const provider = providers.find((item) => item.id === providerId);\n  if (!provider) throw new Error(\"PROVIDER_NOT_FOUND\");\n  const existing = provider.apiKeys.find((item) => item.id === apiKeyId);\n  if (!existing) throw new Error(\"API_KEY_NOT_FOUND\");\n\n  const duplicate = provider.apiKeys.some(\n    (item) => item.id !== apiKeyId && sanitizeApiKey(item.apiKey) === normalizedApiKey,\n  );\n  if (duplicate) throw new Error(\"API_KEY_EXISTS\");\n\n  const now = Date.now();\n  existing.apiKey = normalizedApiKey;\n  if (name !== undefined) {\n    existing.name = sanitizeName(name);\n  }\n  existing.updatedAt = now;","sourceCodeStart":797,"sourceCodeEnd":833,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/services/codexModelProviderService.ts#L797-L833","documentation":"updateApiKeyOnCodexModelProvider sanitizes the supplied secret with sanitizeApiKey and throws API_KEY_REQUIRED when the normalized value is empty — i.e. the call would replace the key with nothing. This is an input-validation guard executed before the provider lookup, so an empty key never reaches the store.","triggerScenarios":"Calling updateApiKeyOnCodexModelProvider(providerId, apiKeyId, apiKey, name?) where sanitizeApiKey(apiKey) returns '' — apiKey is '', whitespace, undefined-as-string, or a value stripped entirely by sanitization (e.g. only characters the sanitizer removes).","commonSituations":"Rotating a key but pasting an empty clipboard; an env var (e.g. PROVIDER_API_KEY) unset so the code passes an empty string; form field cleared then saved; reading the key from a config that returns null coerced to ''.","solutions":["Ensure a non-empty key is passed: check apiKey.trim().length > 0 before calling.","Fix the source of the empty value (set the env var, correct the config path, re-copy the secret).","If you meant to delete the key, use removeApiKeyFromCodexModelProvider instead of updating to an empty value.","Catch 'API_KEY_REQUIRED' and show a 'key must not be empty' prompt in the UI."],"exampleFix":"// before\nawait updateApiKeyOnCodexModelProvider(providerId, keyId, process.env.NEW_KEY ?? '');\n// after\nconst key = process.env.NEW_KEY;\nif (!key || !key.trim()) throw new Error('NEW_KEY must be set to rotate the API key');\nawait updateApiKeyOnCodexModelProvider(providerId, keyId, key.trim());","handlingStrategy":"validation","validationCode":"const key = (apiKey ?? '').trim();\nif (!key) throw new Error('API_KEY_REQUIRED: new key material must be non-empty');","typeGuard":"function isNonEmptySecret(value: unknown): value is string {\n  return typeof value === 'string' && value.trim().length > 0;\n}","tryCatchPattern":"try {\n  await updateApiKeyOnCodexModelProvider(providerId, apiKeyId, apiKey, name);\n} catch (e) {\n  if ((e as Error).message === 'API_KEY_REQUIRED') {\n    showFieldError('apiKey', 'Enter the new API key value');\n  } else throw e;\n}","preventionTips":["Check the secret is non-empty after trimming before any key update.","Fail fast at startup when env vars supplying key material are unset.","Use removeApiKeyFromCodexModelProvider for deletions instead of updating to empty.","Validate clipboard pastes in forms (non-empty, expected prefix) before submitting."],"tags":["validation","api-key","required-field","input-sanitization"],"backgroundTag":"missing-required-argument","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"}