{"record":{"id":"a3c85bc3ee7c50f2","repo":"moeru-ai/airi","slug":"identifier-name-is-used-more-than-once-names","errorCode":null,"errorMessage":"Identifier \"{name}\" is used more than once — names need to be unique.","messagePattern":"Identifier \"(.+?)\" is used more than once — names need to be unique\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/renderer/pages/settings/modules/mcp-config.ts","lineNumber":99,"sourceCode":"\n  return config\n}\n\n/** Builds the persisted MCP config file from editable rows. */\nexport function buildConfigFile(\n  servers: ServerForm[],\n  translateMessage: TranslateMcpMessage,\n): ElectronMcpStdioConfigFile {\n  const config: ElectronMcpStdioConfigFile = { mcpServers: {} }\n  const seenIdentifiers = new Set<string>()\n\n  for (const [index, server] of servers.entries()) {\n    const identifier = server.identifier.trim()\n    if (!identifier)\n      throw new Error(translateMessage('errors.empty-identifier', { index: index + 1 }))\n\n    if (seenIdentifiers.has(identifier))\n      throw new Error(translateMessage('errors.duplicate-identifier', { name: identifier }))\n\n    if (!server.command.trim())\n      throw new Error(translateMessage('errors.empty-command', { name: identifier }))\n\n    seenIdentifiers.add(identifier)\n    config.mcpServers[identifier] = buildServerConfig(server)\n  }\n\n  return config\n}\n\n/** Builds the JSON editor draft while preserving the current draft when form validation fails. */\nexport function syncJsonDraftFromServers(\n  servers: ServerForm[],\n  previousDraft: string,\n  translateMessage: TranslateMcpMessage,\n  formatError: (error: unknown) => string,\n) {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/renderer/pages/settings/modules/mcp-config.ts#L81-L117","documentation":"Thrown by buildConfigFile when a server identifier already exists in seenIdentifiers — two rows share the same trimmed name. The ElectronMcpStdioConfigFile.mcpServers is a keyed object, so duplicate keys would silently overwrite; this guard prevents that data loss.","triggerScenarios":"User entered the same identifier on two server rows (e.g. two 'filesystem' entries); pasted a config JSON that listed one server twice; rename left two rows colliding.","commonSituations":"Copy-pasting a server row without renaming; importing a hand-edited JSON with duplicate keys; merging two configs that share a server name.","solutions":["Rename one of the colliding server rows to a unique identifier.","If the duplicate is unintended, delete the extra row.","Add live duplicate detection in the form (highlight duplicate identifiers before save)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function hasDuplicateIdentifiers(servers: ServerForm[]): boolean {\n  const ids = servers.map(s => s.identifier.trim()).filter(Boolean)\n  return new Set(ids).size !== ids.length\n}","typeGuard":"function areIdentifiersUnique(servers: ServerForm[]): boolean {\n  const seen = new Set<string>()\n  for (const s of servers) {\n    const id = s.identifier.trim()\n    if (!id) continue\n    if (seen.has(id)) return false\n    seen.add(id)\n  }\n  return true\n}","tryCatchPattern":"try {\n  buildConfigFile(servers, t)\n} catch (e) {\n  // highlight duplicate rows in the form\n}","preventionTips":["Live-check for duplicate identifiers as the user types.","Highlight colliding rows before Save is allowed.","Auto-suffix duplicates (e.g. 'filesystem-2') if appropriate."],"tags":["mcp","form-validation","i18n","duplicate","configuration"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}