{"record":{"id":"b0fd3ea854ccf697","repo":"farion1231/cc-switch","slug":"select-a-preset-or-custom-configuration-first","errorCode":null,"errorMessage":"Select a preset or custom configuration first","messagePattern":"Select a preset or custom configuration first","errorType":"validation","errorClass":"PiFormValidationError","httpStatus":null,"severity":"warning","filePath":"src/components/providers/forms/PiProviderForm.tsx","lineNumber":1068,"sourceCode":"      });\n      if (!applied) return;\n      includeCompatRef.current = includeCompat;\n      setProviderCompat(value);\n    },\n    [updateSettingsConfig],\n  );\n\n  const handleProviderKeyChange = useCallback((value: string) => {\n    const normalized = value.toLowerCase().replace(/[^a-z0-9-]/g, \"\");\n    setProviderKey(normalized);\n  }, []);\n\n  const submit = async (identity: ProviderFormData) => {\n    onSubmittingChange?.(true);\n    setFormError(null);\n    try {\n      if (!isEdit && selectedPresetId === null) {\n        throw new PiFormValidationError(t(\"pi.form.selectPresetRequired\"));\n      }\n      if (!parseJsonObject(identity.settingsConfig)) {\n        throw new PiFormValidationError(\n          t(\"jsonEditor.mustBeObject\"),\n          \"#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\"),","sourceCodeStart":1050,"sourceCodeEnd":1086,"githubUrl":"https://github.com/farion1231/cc-switch/blob/0b5da510168914b251481654a568c3ffacd62cf4/src/components/providers/forms/PiProviderForm.tsx#L1050-L1086","documentation":"PiProviderForm throws this PiFormValidationError when submit() runs in create mode (isEdit === false) and selectedPresetId is still null. The Pi provider form is preset-driven: a new provider must either clone a bundled preset or explicitly pick the 'custom' configuration before any other validation runs. It is the first gate in the submit() chain, so nothing else is validated until it passes.","triggerScenarios":"Clicking the form's submit/confirm button in the 'Add provider' (create) flow without ever interacting with the preset selector, so selectedPresetId stays null. Only fires on create; editing an existing provider (isEdit) skips the check entirely.","commonSituations":"A UI regression or custom launcher invokes submit() before the preset step is completed; automation/tests call submit() directly on a fresh form; the preset picker was skipped because the dialog was opened in an unexpected state.","solutions":["In the create flow, choose a preset (or the 'custom configuration' option) so selectedPresetId becomes non-null, then resubmit.","If you are driving the form programmatically, set the preset selection state before invoking submit().","If users should be able to submit without a preset, pass an explicit 'custom' preset id instead of leaving selectedPresetId null."],"exampleFix":"// before\nconst [selectedPresetId, setSelectedPresetId] = useState<string | null>(null);\n// user clicks submit -> throws 'Select a preset or custom configuration first'\n\n// after\nconst [selectedPresetId, setSelectedPresetId] = useState<string | null>(CUSTOM_PRESET_ID);\n// or disable submit until a choice is made:\n<button disabled={!isEdit && selectedPresetId === null}>Save</button>","handlingStrategy":"validation","validationCode":"const canSubmit = isEdit || selectedPresetId !== null;\n// render: <button disabled={!canSubmit}>Save</button>","typeGuard":null,"tryCatchPattern":"try {\n  await submit(identity);\n} catch (e) {\n  if (e instanceof PiFormValidationError) {\n    // e.fieldSelector is undefined here; prompt user to pick a preset\n    setFormError(e.message);\n    return;\n  }\n  throw e;\n}","preventionTips":["Disable the submit button until a preset or the custom option is selected.","Default selectedPresetId to 'custom' if your flow never needs the preset step."],"tags":["pi","form-validation","provider-config","react"],"backgroundTag":"required-form-field-missing","analyzedSha":"0b5da510168914b251481654a568c3ffacd62cf4","analyzedAt":"2026-08-20T14:29:00.113Z","contentChangedAt":"2026-08-20T14:29:00.113Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}