Incident: Bots Telegram Mudos — Contaminação por active_profile

Data: 2026-06-13 Duração: ~8h (detectado manhã, resolvido tarde) Severidade: Alta — ambos os bots (@nexus_zimbot e @light_zimbot) ficaram sem responder Status: ✅ Resolvido

TL;DR

O arquivo ~/.hermes/active_profile ficou escrito como "light". O hermes_constants.py usa isso como fallback quando o HERMES_HOME env var não bate — mesmo com o env var certo setado no systemd unit. Resultado: o gateway do perfil default conectou ao Telegram usando o token do perfil light (@light_zimbot), ficou em polling ativo, e nunca processou as mensagens do @nexus_zimbot. Estado reportava connected (mentira) e getMe direto confirmava o bot errado conectado.

Sintomas observados

  • Token funciona em teste isolado: curl /getMeok: true
  • gateway_state.jsontelegram: state: "connected"
  • Mas o bot não respondia a /start (nem após minutos)
  • getUpdates direto → result: [] (nenhuma mensagem pendente)
  • O outro bot do setup estava respondendo normalmente
  • Log do gateway default: nenhum “Received update” relativo ao bot certo
  • Service systemd: active (running), sem crash

Diagnóstico (passo a passo)

# 1. Verificar o estado global do active_profile
cat /home/ubuntu/.hermes/active_profile
# Saiu: "light" ← PROBLEMA
 
# 2. Ver onde o gateway default acha que está (paths reais via /proc)
ls -la /proc/<PID_GATEWAY>/fd/ | grep -E "gateway\.log|\.env|state\.db$"
# Apareceu: /home/ubuntu/.hermes/profiles/light/state.db
# Confirmou: o default tava rodando no diretório do light
 
# 3. Confirmar HERMES_HOME por processo
cat /proc/<PID>/environ | tr '\0' '\n' | grep HERMES_HOME
# default: HERMES_HOME=/home/ubuntu/.hermes (env var correta)
# Mas o active_profile fez fallback pro light
 
# 4. Confirmar tokens em cada .env
grep TELEGRAM_BOT_TOKEN /home/ubuntu/.hermes/.env
# 828134... → @nexus_zimbot
grep TELEGRAM_BOT_TOKEN /home/ubuntu/.hermes/profiles/light/.env
# 850769... → @light_zimbot

Causa raiz

O hermes_constants.py (lido por 30+ módulos no import-time) tem um fallback silencioso: se HERMES_HOME env var não bate, consulta ~/.hermes/active_profile. Se ele aponta pro perfil errado, o gateway redireciona:

  • config.yaml → config do outro perfil
  • .env → token do outro bot
  • gateway.lock → diretório do outro perfil
  • gateway.log → log do outro perfil
  • gateway_state.json → state do outro perfil

O systemd unit do gateway default seta HERMES_HOME=/home/ubuntu/.hermes corretamente, mas o fallback do Hermes lê o active_profile como âncora secundária. Resultado: o gateway conecta, conecta com token válido (o do light!), fica em polling, e nunca processa mensagem do @nexus_zimbot — porque a API do Telegram não entrega update pra bot que tem outro getMe.

Por que o connected mentia: python-telegram-bot faz getMe no startup, recebe sucesso (token existe, é um bot válido), e marca connected. Não tem como saber que é o bot “errado” do ponto de vista do usuário.

Correção (sequência executada)

# 1. Corrigir o active_profile
echo "default" > /home/ubuntu/.hermes/active_profile
 
# 2. Parar TUDO (evita crash loop durante o fix)
systemctl --user stop hermes-gateway.service hermes-gateway-light.service
 
# 3. Limpar locks e state files stale dos DOIS perfis
rm -f /home/ubuntu/.hermes/profiles/light/gateway.lock \
      /home/ubuntu/.hermes/profiles/light/gateway.pid \
      /home/ubuntu/.hermes/profiles/light/gateway_state.json \
      /home/ubuntu/.hermes/gateway.lock \
      /home/ubuntu/.hermes/gateway.pid \
      /home/ubuntu/.hermes/gateway_state.json
 
# 4. Subir o light PRIMEIRO (ele é menos crítico; default costuma ter mais dependências)
systemctl --user start hermes-gateway-light.service
sleep 3
systemctl --user start hermes-gateway.service
 
# 5. Verificar
sleep 5
cat /home/ubuntu/.hermes/gateway_state.json | python3 -m json.tool | grep -A1 telegram
cat /home/ubuntu/.hermes/profiles/light/gateway_state.json | python3 -m json.tool | grep -A1 telegram
# Ambos devem mostrar state: "connected"
 
# 6. Validar que cada processo tá no diretório certo
for pid in $(pgrep -f "hermes_cli.main gateway"); do
  echo "PID $pid:"
  ls -la /proc/$pid/fd/ | grep -E "gateway\.log|state\.db$" | head -2
  cat /proc/$pid/environ | tr '\0' '\n' | grep HERMES_HOME
done

Validação pós-fix

# Cada bot responde com seu username via getMe
cd /home/ubuntu/.hermes/hermes-agent && ./venv/bin/python3 -c "
import os, asyncio, httpx
from dotenv import load_dotenv
load_dotenv('/home/ubuntu/.hermes/.env')  # e depois /profiles/light/.env
token = os.getenv('TELEGRAM_BOT_TOKEN', '')
async def t():
    async with httpx.AsyncClient() as c:
        r = await c.get(f'https://api.telegram.org/bot{token}/getMe')
        d = r.json()
        print(f'@{d[\"result\"][\"username\"]} ok={d[\"ok\"]}')
asyncio.run(t())
"
# default: @nexus_zimbot
# light:   @Light_ZimBOT
 
# /start no @nexus_zimbot → resposta
# /start no @light_zimbot → resposta (ZimBOT-Light)

Lições aprendidas

  1. Nunca confiar em gateway_state.json “connected” como sinal de saúde. O bot pode estar conectado com token errado. O estado reflete “o python-telegram-bot conseguiu fazer getMe”, não “o bot certo está respondendo”.
  2. Sempre checar /proc/<PID>/fd/ pra ver onde o processo realmente está escrevendo — é o diagnóstico mais confiável quando algo está silenciosamente errado.
  3. active_profile é uma âncora global perigosa. Mudou em qualquer lugar (heartbeat, script, setup) afeta TODOS os gateways do sistema, não só o interativo.
  4. Testes isolados enganam. getMe retornando ok confirma que o token é válido, não que o bot certo está respondendo.
  5. Contaminação cross-profile é silenciosa. Não dá erro, não dá log de aviso pro usuário — só fica mudo. A skill active-profile-contamination foi criada pra capturar isso.

Prevenção (recomendações)

  • Adicionar check no heartbeat que valide se active_profile bate com o HERMES_HOME do systemd unit do default gateway. Se não bater, alerta imediato.
  • Adicionar entry check na skill hermes-troubleshooting que avise pro agente verificar isso PRIMEIRO quando o sintoma for “bot mudo mas state=connected”.
  • Considerar mudar o Hermes pra abortar startup se active_profile e HERMES_HOME divergem. Mas é mudança no core.

Referências

  • Skill nova: ~/.hermes/profiles/light/skills/hermes/active-profile-contamination/SKILL.md
  • Skill atualizada: ~/.hermes/profiles/light/skills/hermes/hermes-troubleshooting/SKILL.md (pointer pra nova skill)
  • Sessão original do bug: 20260613_041333_ae766237 (deepseek-v4-flash, perfil default, 13/jun 01:13 AM)
  • Sessão da correção: sessão atual (MiniMax-M3, perfil light, 13/jun ~17:00)
  • Source do bug no Hermes: hermes_constants.py linha 53-100, função get_hermes_home() com fallback pro active_profile