{"record":{"id":"853b726ff9ab6f89","repo":"farion1231/cc-switch","slug":"model-id-id-is-duplicated","errorCode":null,"errorMessage":"Model ID {{id}} is duplicated","messagePattern":"Model ID (.+?)\\} is duplicated","errorType":"validation","errorClass":"PiFormValidationError","httpStatus":null,"severity":"error","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/0b5da510168914b251481654a568c3ffacd62cf4/src/components/providers/forms/PiProviderForm.tsx#L1100-L1136","documentation":"PiProviderForm deduplicates model ids per provider: it walks normalizedModels with a seen Set and throws pi.form.duplicateModel (with the offending id, focusing #pi-model-id-<rowKey>) when seen.has(id) is true. Duplicate ids would make Pi's per-model routing and settings ambiguous, so the form rejects them before persisting. Comparison is exact and case-sensitive because ids are opaque strings.","triggerScenarios":"Two rows both containing id 'gpt-4o'; pasting the same model twice; copy-duplicating a row and editing only its display name; ids that differ only by whitespace are NOT duplicates (no trimming) but 'X' vs 'x' ARE distinct, so case-typos like 'GPT-4o' vs 'gpt-4o' pass while exact repeats fail.","commonSituations":"Adding the same underlying model with different display names for different reasoning settings; import merges that concat model lists; manual re-entry forgetting a row already exists.","solutions":["Find the second row whose ID matches {id} in the error and either change it to a genuinely different model id or delete the duplicate row.","If you need two configurations of the same model, that is not supported at the id level — pick one or use Pi-side profiles.","Audit the model table for near-identical rows before submitting large imported lists."],"exampleFix":"// before\nconst models = [\n  { key: \"m1\", id: \"gemini-2.5-pro\", name: \"Fast\" },\n  { key: \"m2\", id: \"gemini-2.5-pro\", name: \"Slow\" }, // duplicate id\n];\nsubmit(identity); // throws \"Model ID gemini-2.5-pro is duplicated\"\n\n// after\nconst models = [\n  { key: \"m1\", id: \"gemini-2.5-pro\", name: \"Fast\" },\n  { key: \"m2\", id: \"gemini-2.5-flash\", name: \"Slow\" },\n];\nsubmit(identity);","handlingStrategy":"validation","validationCode":"const ids = models.map((m) => m.id);\nconst dup = ids.find((id, i) => ids.indexOf(id) !== i);\nif (dup) markRows(`[data-id=\"${dup}\"]`, \"Duplicate model id\");","typeGuard":null,"tryCatchPattern":"catch (e) {\n  if (e instanceof PiFormValidationError) { setFormError(e.message); /* e.fieldSelector targets the duplicate row */ document.querySelector(e.fieldSelector ?? \"\")?.focus(); return; }\n  throw e;\n}","preventionTips":["Show a live 'duplicate' badge on rows whose id already exists in the list.","Run a Set-based dedupe pass over imported model lists before hydrating the form."],"tags":["pi","form-validation","duplicate-key","model-id"],"backgroundTag":"duplicate-identifier-conflict","analyzedSha":"0b5da510168914b251481654a568c3ffacd62cf4","analyzedAt":"2026-08-20T14:29:00.113Z","contentChangedAt":"2026-08-20T14:29:00.113Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}