{"record":{"id":"347c308f37347dfc","repo":"farion1231/cc-switch","slug":"pi-form-duplicatemodel","errorCode":null,"errorMessage":"pi.form.duplicateModel","messagePattern":"pi\\.form\\.duplicateModel","errorType":"validation","errorClass":"PiFormValidationError","httpStatus":null,"severity":"warning","filePath":"src/components/providers/forms/PiProviderForm.tsx","lineNumber":1118,"sourceCode":"          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;\n        if (id.length === 0) {\n          throw new PiFormValidationError(\n            t(\"pi.form.modelIdRequired\", { index: index + 1 }),\n            `#pi-model-id-${model.key}`,\n            true,\n          );\n        }\n        if (seen.has(id)) {\n          throw new PiFormValidationError(\n            t(\"pi.form.duplicateModel\", { id }),\n            `#pi-model-id-${model.key}`,\n            true,\n          );\n        }\n        seen.add(id);\n        const displayName = model.name.trim();\n        const includeName = !isEdit || model.hasName;\n        const includeReasoning = !isEdit || model.hasReasoning;\n        const includeInput = !isEdit || model.hasInput;\n        const includeContextWindow = !isEdit || model.hasContextWindow;\n        const includeMaxTokens = !isEdit || model.hasMaxTokens;\n        if (includeName && !displayName) {\n          throw new PiFormValidationError(\n            t(\"pi.form.modelNameRequired\", { index: index + 1 }),\n            `#pi-model-name-${model.key}`,\n            true,\n          );","sourceCodeStart":1100,"sourceCodeEnd":1136,"githubUrl":"https://github.com/farion1231/cc-switch/blob/a2e22f330273a5b6ffa87cb8b82b624601bac562/src/components/providers/forms/PiProviderForm.tsx#L1100-L1136","documentation":"Thrown per-model in PiProviderForm's submit() when two model rows share the exact same id string (PiProviderForm.tsx:1117-1124). Ids feed a Set ('seen') during normalization, and Pi's settings require unique model keys. Comparison is exact and case-sensitive: 'GPT' and 'gpt' are distinct and both pass. The message interpolates the duplicated id; fieldSelector '#pi-model-id-{model.key}' with revealAdvanced=true focuses the offending row.","triggerScenarios":"Adding the same endpoint model twice; copy-pasting a row to tweak settings without changing its ID; importing a config that already contained a duplicate entry.","commonSituations":"User wants two configurations of one model (must differentiate ids); merged imports from two sources with overlapping model lists; accidental double-click on an add/import action.","solutions":["Rename or delete the duplicate row identified by the error message","If two configs of one model are intended, give them distinct ids (for example a '-reasoning' suffix)","Dedupe rows by id (case-sensitively) before enabling Save"],"exampleFix":"// before\nconst ids = models.map((m) => m.id);\n\n// after (block duplicates before submit)\nconst ids = models.map((m) => m.id);\nconst hasDupes = new Set(ids).size !== ids.length;\n<button type=\"submit\" disabled={hasDupes}>Save</button>;","handlingStrategy":"validation","validationCode":"const seen = new Set<string>();\nfor (const [i, m] of models.entries()) {\n  if (seen.has(m.id)) {\n    setFormError(t(\"pi.form.duplicateModel\", { id: m.id }));\n    return;\n  }\n  seen.add(m.id);\n}","typeGuard":"function hasUniqueModelIds(models: Array<{ id: string }>): boolean {\n  return new Set(models.map((m) => m.id)).size === models.length;\n}","tryCatchPattern":"try {\n  await submit(identity);\n} catch (error) {\n  if (error instanceof Error && error.name === \"PiFormValidationError\") { /* message names the duplicated id; fieldSelector focuses that row */ return; }\n  throw error;\n}","preventionTips":["Check duplicates case-sensitively — the backend Set is exact-match","Warn inline as soon as a second row repeats an existing id","When copying a row for a variant config, force an id suffix"],"tags":["form-validation","pi","models","duplicates"],"backgroundTag":null,"analyzedSha":"a2e22f330273a5b6ffa87cb8b82b624601bac562","analyzedAt":"2026-08-16T03:46:07.889Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}