{"record":{"id":"7c7bec3b75aa46dc","repo":"moeru-ai/airi","slug":"server-index-is-missing-an-identifier","errorCode":null,"errorMessage":"Server # {index} is missing an identifier.","messagePattern":"Server # (.+?) is missing an identifier\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/renderer/pages/settings/modules/mcp-config.ts","lineNumber":96,"sourceCode":"\n  if (!server.enabled)\n    config.enabled = false\n\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,","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/renderer/pages/settings/modules/mcp-config.ts#L78-L114","documentation":"Thrown by buildConfigFile while iterating MCP server form rows: a row's identifier (after trim) is empty. The message is i18n-driven via translateMessage('errors.empty-identifier', { index }), so the literal text varies by locale; the English default reads 'Server #N is missing an identifier.'.","triggerScenarios":"User added an MCP server row in settings but left the identifier/name field blank, then saved. Each row is validated in order; the first empty identifier aborts the build.","commonSituations":"Form validation gap — save enabled before identifier entered; JSON-draft sync created a row with empty identifier; user cleared a row's name to rename it and saved prematurely.","solutions":["Fill in a unique identifier for every server row before saving.","Disable the Save button until all rows have non-empty, unique identifiers.","Remove empty rows from the form before buildConfigFile runs.","Pre-trim and validate identifiers in the form's reactive state."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function allServersHaveIdentifier(servers: ServerForm[]): boolean {\n  return servers.every(s => s.identifier.trim().length > 0)\n}","typeGuard":"function isNonEmptyIdentifier(id: string): boolean {\n  return typeof id === 'string' && id.trim().length > 0\n}","tryCatchPattern":"try {\n  buildConfigFile(servers, t)\n} catch (e) {\n  // e.message is the i18n string; surface next to the offending row\n}","preventionTips":["Disable Save until every row has a non-empty trimmed identifier.","Trim identifiers in the form's reactive state on input.","Remove blank rows before calling buildConfigFile."],"tags":["mcp","form-validation","i18n","configuration"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}