{"record":{"id":"34c88cc26872fb88","repo":"ruvnet/ruflo","slug":"transport-name-already-exists-34c88c","errorCode":null,"errorMessage":"Transport \"${name}\" already exists","messagePattern":"Transport \"(.+?)\" already exists","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/shared/src/mcp/transport/index.ts","lineNumber":146,"sourceCode":"export function createInProcessTransport(logger: ILogger): ITransport {\n  return new InProcessTransport(logger);\n}\n\n/**\n * Transport manager for multi-transport scenarios\n */\nexport class TransportManager {\n  private transports: Map<string, ITransport> = new Map();\n  private running = false;\n\n  constructor(private readonly logger: ILogger) {}\n\n  /**\n   * Add a transport\n   */\n  addTransport(name: string, transport: ITransport): void {\n    if (this.transports.has(name)) {\n      throw new Error(`Transport \"${name}\" already exists`);\n    }\n    this.transports.set(name, transport);\n    this.logger.debug('Transport added', { name, type: transport.type });\n  }\n\n  /**\n   * Remove a transport\n   */\n  async removeTransport(name: string): Promise<boolean> {\n    const transport = this.transports.get(name);\n    if (!transport) {\n      return false;\n    }\n\n    await transport.stop();\n    this.transports.delete(name);\n    this.logger.debug('Transport removed', { name });\n    return true;","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/shared/src/mcp/transport/index.ts#L128-L164","documentation":"TransportManager keeps transports in a Map keyed by a caller-chosen unique name. addTransport(name, transport) throws when the name is already present rather than silently replacing a possibly-running transport. Presence can be checked read-only via getTransportNames()/getTransport(name), and entries are removed with removeTransport(name).","triggerScenarios":"Two setup functions both registering a transport named 'http'; re-running a registration routine after a partial failure where the first add already succeeded; names generated only from the transport type (not per-instance) so a second listener of the same type collides.","commonSituations":"Plugin/module systems where several modules register transports into one manager; init retries; config reload re-running wiring; name derived from a value that does not change between re-registrations.","solutions":["Check membership first: if (!manager.getTransportNames().includes(name)) manager.addTransport(name, t)","Name transports by role + instance (http-main, http-metrics) so distinct transports cannot collide","For re-registration flows, await manager.removeTransport(name) first, then add the new instance"],"exampleFix":"// before\nmanager.addTransport('http', httpA);\nmanager.addTransport('http', httpB); // throws: already exists\n\n// after\nmanager.addTransport('http-main', httpA);\nmanager.addTransport('http-metrics', httpB);","handlingStrategy":"validation","validationCode":"if (manager.getTransportNames().includes(name)) {\n  throw new Error(`transport '${name}' already registered - pick a unique name`);\n}\nmanager.addTransport(name, transport);","typeGuard":null,"tryCatchPattern":"try {\n  manager.addTransport(name, transport);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('already exists')) {\n    return; // idempotent registration for restart flows\n  }\n  throw e;\n}","preventionTips":["Derive transport names from role + instance, not a constant","Presence-check with getTransportNames() before add in idempotent init code","Treat duplicate-name throws as a wiring bug to fix, not to silence routinely"],"tags":["mcp","transport","registry","duplicate-key","naming"],"backgroundTag":"duplicate-registration","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}