{"record":{"id":"5aac680b722f7e23","repo":"farion1231/cc-switch","slug":"pi-form-credentialrequired","errorCode":null,"errorMessage":"pi.form.credentialRequired","messagePattern":"pi\\.form\\.credentialRequired","errorType":"validation","errorClass":"PiFormValidationError","httpStatus":null,"severity":"warning","filePath":"src/components/providers/forms/PiProviderForm.tsx","lineNumber":1091,"sourceCode":"          \"#pi-settings-config\",\n        );\n      }\n      const trimmedName = identity.name.trim();\n      const trimmedKey = providerKey.trim();\n      if (!trimmedName) {\n        throw new PiFormValidationError(\n          t(\"pi.form.nameRequired\"),\n          'input[name=\"name\"]',\n        );\n      }\n      if (!isEdit && !trimmedKey) {\n        throw new PiFormValidationError(\n          t(\"pi.form.providerKeyRequired\"),\n          \"#pi-provider-key\",\n        );\n      }\n      if (!isEdit && selectedPreset && apiKey.length === 0) {\n        throw new PiFormValidationError(\n          t(\"pi.form.credentialRequired\"),\n          \"#pi-api-key\",\n        );\n      }\n      if (!isEdit && models.length === 0) {\n        throw new PiFormValidationError(\n          t(\"pi.form.modelRequired\"),\n          \"#pi-add-model\",\n          true,\n        );\n      }\n\n      const headers = normalizeRequestHeaders(providerHeaders);\n      const seen = new Set<string>();\n      const normalizedModels = models.map((model, index) => {\n        // Pi treats model IDs as opaque strings. Trimming would rename an\n        // imported model.\n        const id = model.id;","sourceCodeStart":1073,"sourceCodeEnd":1109,"githubUrl":"https://github.com/farion1231/cc-switch/blob/a2e22f330273a5b6ffa87cb8b82b624601bac562/src/components/providers/forms/PiProviderForm.tsx#L1073-L1109","documentation":"Thrown by PiProviderForm's submit() when creating (!isEdit) with a real preset selected (selectedPreset non-null) and the API key field still empty (PiProviderForm.tsx:1089-1094). Presets describe hosted endpoints that require a credential, so the form blocks creation until one is supplied. Choosing 'Custom' (which leaves selectedPreset null) skips this requirement even in create mode. fieldSelector '#pi-api-key' focuses the key input.","triggerScenarios":"Selecting a hosted preset and clicking Save before pasting an API key; using a key-helper flow but leaving the manual key empty when the preset has no apiKeyUrl; switching presets after clearing the key.","commonSituations":"User intends to fetch the key later; user selects the wrong preset for a local/no-auth gateway; key stored in a password manager and the paste was missed.","solutions":["Paste the provider's API key into the credential field, then save","Open the preset's apiKeyUrl (form shows the link when selectedPreset.apiKeyUrl exists) to obtain a key","Switch the preset to 'Custom' if the endpoint genuinely needs no credential"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!isEdit && selectedPreset !== null && apiKey.length === 0) {\n  setFormError(t(\"pi.form.credentialRequired\"));\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await submit(identity);\n} catch (error) {\n  if (error instanceof Error && error.name === \"PiFormValidationError\") { /* focus '#pi-api-key' */ return; }\n  throw error;\n}","preventionTips":["Show the preset's apiKeyUrl link prominently so users fetch the key before saving","Disable Save when a hosted preset is selected and the credential is empty","Remember that 'Custom' intentionally skips the credential check"],"tags":["form-validation","pi","api-key","credentials"],"backgroundTag":null,"analyzedSha":"a2e22f330273a5b6ffa87cb8b82b624601bac562","analyzedAt":"2026-08-16T03:46:07.889Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}