Volver al blogMTProto

AUTH_KEY_DUPLICATED: el error silencioso que mata tu cuenta de Telegram

Por qué tu cuenta se desconecta sola, qué hace Telegram cuando detecta dos conexiones con la misma clave y cómo evitarlo en producción.

Robinson Silverio24 de mayo de 20266 min de lectura
AUTH_KEY_DUPLICATED: el error silencioso que mata tu cuenta de Telegram

Si llevas más de un mes operando un userbot en producción, vas a ver este error: AUTH_KEY_DUPLICATED. No tiene mensaje explicativo, no aparece en la docs pública de Telegram con claridad, y silenciosamente desconecta tu cuenta. Es la causa #1 de pérdida de sesiones MTProto en SaaS de Telegram.

Esta guía explica qué es exactamente, por qué pasa, qué hace Telegram cuando lo detecta y cómo evitarlo.

Qué dice Telegram, técnicamente

Telegram impone una regla estricta a nivel de protocolo: una auth_key solo puede tener una conexión activa a la vez. Cuando un segundo cliente intenta usar la misma auth_key:

  1. El servidor de Telegram detecta dos clientes con el mismo identificador
  2. Devuelve AUTH_KEY_DUPLICATED a la conexión más antigua
  3. Mata las dos conexiones
  4. Si pasa repetidamente, Telegram empieza a marcar la sesión como sospechosa

El problema: GramJS y la mayoría de los clientes MTProto no levantan una excepción "fatal" — devuelven un disconnect silencioso. Tu app cree que la cuenta sigue válida, pero está desconectada.

Por qué pasa en producción

Las tres causas más comunes:

Causa 1: Dos workers cargan la misma sesión a la vez

Tienes una cola con tareas y N workers consumiendo. Una tarea para la cuenta A llega al worker 1. Mientras está procesando, llega otra tarea para la cuenta A al worker 2. Worker 2 carga la sesión, abre cliente MTProto. Boom: AUTH_KEY_DUPLICATED.

Frecuencia: si tienes 5 cuentas y 1 worker, casi nunca. Con 50 cuentas y 4 workers procesando una cola compartida, decenas de veces al día.

Causa 2: Sesión cargada en frontend Y en backend

Algunos productos cargan la sesión en el navegador del usuario (para "preview en vivo") además del backend que envía mensajes. Cada vez que el usuario abre la app, abre conexión. Si el backend también abre, choque.

Solución obvia: sesiones MTProto nunca se cargan en el frontend. Solo en backend.

Causa 3: Reconexión sin esperar el disconnect previo

Tu worker se cae con un error, se reinicia automáticamente, abre cliente con la misma sesión inmediatamente. Telegram aún tiene la conexión vieja medio-viva (TCP timeout normal: 60-120s). Resultado: dos conexiones simultáneas brevemente, AUTH_KEY_DUPLICATED.

Solución: esperar al menos 30s entre disconnect y reconnect del mismo auth_key.

Lo que pasa después: la cascada

Una vez Telegram emite AUTH_KEY_DUPLICATED en tu cuenta:

  1. Primera vez: las dos conexiones se cortan, todo intento de reconnect inmediato falla
  2. Segunda vez en pocos minutos: la sesión empieza a fallar en getMe() con error sutil
  3. Tres-cuatro veces: Telegram invalida la sesión. StringSession deja de funcionar
  4. Cinco veces en una hora: la cuenta puede entrar en bandera "sospechosa" y recibir FloodWait largos

Hemos visto cuentas perder validez en menos de 10 minutos cuando el bug del lock no estaba resuelto y la cola tenía decenas de tareas para la misma cuenta.

La solución: lock distribuido por cuenta

El patrón correcto, probado en producción:

import { redis } from "@/lib/redis";
 
const TTL_LOCK_SEGUNDOS = 30;
const ESPERA_MAX_MS = 5000;
 
export async function conLockConexionMTProto<T>(
  accountId: string,
  fn: () => Promise<T>
): Promise<T> {
  const key = `mtproto:lock:${accountId}`;
  const inicio = Date.now();
 
  while (Date.now() - inicio < ESPERA_MAX_MS) {
    const ok = await redis.set(key, process.pid, "EX", TTL_LOCK_SEGUNDOS, "NX");
    if (ok) {
      try {
        return await fn();
      } finally {
        await redis.del(key);
      }
    }
    await new Promise((r) => setTimeout(r, 100));
  }
 
  throw new Error(`Lock no disponible para cuenta ${accountId}`);
}

Cada cliente MTProto se abre dentro de conLockConexionMTProto(accountId, ...). Si dos workers compiten por la misma cuenta, uno espera hasta que el otro termina (o falla después de 5s).

El TTL de 30s es crítico: si un proceso se cae sin liberar el lock, Redis lo expira automáticamente. Sin TTL, una caída deja un lock huérfano para siempre.

Patrones más sutiles

Lock + best-effort fallback

A veces el lock no está disponible por más de 5s (alta concurrencia) y prefieres seguir adelante. Cuidado: ese fallback es el que genera AUTH_KEY_DUPLICATED. Solo úsalo si:

  • Es para operaciones read-only que no abren conexión nueva (consultar caché de mensajes ya enviados)
  • Tienes alarma activa: si el fallback dispara > N veces/hora, escalar

Singleton del cliente

Otra técnica: mantener una sola instancia de cliente MTProto por accountId en memoria del worker. Las tareas la reusan en vez de abrir/cerrar.

const clientes = new Map<string, TelegramClient>();
 
async function obtenerCliente(accountId: string) {
  if (!clientes.has(accountId)) {
    const cliente = await crearCliente(accountId);
    clientes.set(accountId, cliente);
  }
  return clientes.get(accountId)!;
}

Combinado con lock distribuido, este patrón elimina el 99% de los AUTH_KEY_DUPLICATED.

Disconnect graceful con esperas

Al cerrar, no llames disconnect() y abras inmediatamente otra conexión:

await client.disconnect();
await new Promise((r) => setTimeout(r, 1000));
// ahora puedes reconnect si necesario

El segundo (1000ms) le da a Telegram tiempo de procesar el cierre del TCP.

Cómo recuperar una sesión que ya cayó

Si una sesión ya recibió AUTH_KEY_DUPLICATED múltiples veces y ahora falla:

  1. Intenta un client.connect() limpio (a veces vuelve)
  2. Si falla con AUTH_KEY_UNREGISTERED, la sesión está muerta: el usuario tiene que reautenticarse con SMS
  3. Si vuelve a conectar, marca la cuenta como "warmup" durante 24h: bájale la cuota de envíos al 50% para no agravar

No hay magia: una sesión muerta es muerta. La prevención es lo único que escala.

Observability mínimo

Tu app debe alertarte de esto antes de perder cuentas:

  • Métrica: número de AUTH_KEY_DUPLICATED por hora
  • Alerta: si > 3 en 10min, página al on-call
  • Dashboard: lista de cuentas que han disparado el error en las últimas 24h
  • Log estructurado: cada disparo con accountId, timestamp y worker_pid

Esto te permite encontrar el bug en horas, no en semanas.

La pregunta clave para evaluar herramientas

Si vas a contratar un SaaS de Telegram, pregúntales:

"¿Cómo manejan AUTH_KEY_DUPLICATED y cuántos ven por mes?"

Las respuestas posibles:

  • "¿Qué es eso?" → corre. No tienen producción real.
  • "Lo manejamos con retry" → mal. Retry sin lock empeora el problema.
  • "Tenemos lock distribuido con Redis y singleton de cliente, vemos < 5 al mes en 10K cuentas" → buena señal.

Vega Punk y AUTH_KEY_DUPLICATED

En Vega Punk usamos conLockConexionMTProto con Redis + TTL 30s en todos los puntos donde abrimos cliente: workers, API routes que necesitan MTProto, scripts de mantenimiento. Antes del fix, veíamos ~50 por día. Después: < 2 por semana, y todos por casos de borde resueltos en el siguiente release.

La diferencia entre producto funcional y producto profesional es manejar exactamente este tipo de error con detalle.

Conclusión

AUTH_KEY_DUPLICATED no es un bug raro: es estructural si tu arquitectura no lo previene. Lock distribuido + singleton de cliente + observability son la tríada que lo elimina.

Si tu SaaS de Telegram tiene clientes quejándose de "mi cuenta se desconectó sola", probablemente sea esto. Resolverlo cuesta 2-3 días de un dev senior. No resolverlo cuesta clientes.

#mtproto#auth_key#telegram#errores