Command not found no terminal do agent: como o Hermes corrigiu um PATH redefinido pelos login shells

Você pede ao agent que rode hermes cron list no terminal dele para ver por que um job agendado nunca disparou. A resposta é uma linha só: bash: hermes: command not found. Você abre a sua própria janela SSH, digita a mesma coisa e funciona. De volta à session, command -v node também volta vazio — mesmo com o node instalado via nvm.
Não é problema de permissão e o Hermes não está quebrado. Alguém redefiniu o PATH, de forma silenciosa e completa.
Quem redefiniu o PATH
Por trás da tool de terminal existe um bash. Para que as configurações de nvm, asdf e pyenv das quais você depende também funcionem para o agent, o Hermes inicia um login shell uma vez, quando cria a session, e captura o ambiente resultante — carregar ~/.profile, ~/.bash_profile e ~/.bashrc é exatamente como esses gerenciadores de versão se adicionam ao PATH. Essa parte é intencional e útil.
O problema é o /etc/profile do Debian e do Ubuntu: ele não estende o PATH, ele reescreve o PATH como uma lista fixa (normalmente /usr/local/bin:/usr/bin:/bin mais alguns diretórios de games). Quando o login shell termina, o snapshot capturado fixa esse PATH redefinido. Tudo o que o Hermes tinha na inicialização some: ~/.local/bin, /opt/hermes/bin, /opt/hermes/.venv/bin e todos os diretórios de shim do nvm.
Na imagem Docker o sintoma é o mais escandaloso: entre em um container com bash -l, ou docker exec … bash -l, e o hermes puro sai com 127. Comandos em background sofrem igual, porque rodam via bash -lic.
A correção: recolocar o que o profile descartou
A reparação não sobrescreve o que o login shell decidiu. Ela concilia os dois:
- depois que o profile de login roda, qualquer diretório do ambiente de lançamento que tenha sumido é anexado no fim, na ordem relativa original;
- diretórios que já estão presentes nunca são duplicados;
- só entradas absolutas têm a existência verificada — uma entrada relativa é resolvida a partir do diretório de trabalho do shell, então checá-la a partir do Python não faria sentido;
- diretórios ausentes são ignorados em vez de serem enfiados no PATH como peso morto.
Anexar no fim é a escolha que importa. O que o init do seu nvm ou pyenv decidiu continua exatamente onde estava, então um comando que resolvia antes continua resolvendo para o mesmo binário; só ganham correspondência os comandos que não tinham nenhuma.
Comandos em background (o spawn_local do process_registry, bash -lic) recebem o mesmo trecho de restauração na frente, então foreground e background não conseguem discordar sobre qual python ou hermes existe.
A imagem Docker adiciona uma segunda camada: o /etc/profile.d/hermes.sh é instalado na imagem, então todo login shell recoloca os três diretórios da imagem — deixando onde estão os que já aparecem na lista.
# /etc/profile.d/hermes.sh (dentro da imagem)
for __hermes_p in /opt/data/.local/bin /opt/hermes/.venv/bin /opt/hermes/bin; do
case ":$PATH:" in
*":$__hermes_p:"*) ;;
*) PATH="$__hermes_p:$PATH" ;;
esac
done
unset __hermes_p
export PATH
Confira como o PATH está de verdade
Três linhas dentro do terminal do agent contam a história inteira:
echo $PATH # o que o agent realmente recebe
command -v hermes # vazio significa não encontrado
bash -lc 'echo $PATH' # o PATH redefinido pelo login shell, para comparação
Tabela de sintoma para causa:
| Sintoma | Causa real | O que fazer |
|---|---|---|
hermes: command not found dentro do Docker |
O login shell redefiniu o PATH; a imagem não tem o patch de profile.d | Atualize a imagem ou o build do Hermes |
node / python ausentes mesmo instalados no host |
O /etc/profile atropelou o init do nvm/pyenv vindo do ~/.profile |
Corrigido automaticamente no build novo; em builds antigos, liste os arquivos de init explicitamente |
| Só os comandos em background falham | O build antigo não compartilhava o trecho de restauração com o caminho de background | Atualize, ou use comandos em foreground por enquanto |
| O seu próprio script não encontra uma tool | O script contava com um diretório que só um shell interativo tem | Use caminhos absolutos, ou adicione o arquivo em shell_init_files |
Se você tem arquivos de ambiente customizados — uma toolchain interna, por exemplo — diga ao Hermes exatamente o que carregar em vez de esperar que ele adivinhe:
terminal:
auto_source_bashrc: true # padrão; uma lista explícita em shell_init_files assume o controle
shell_init_files:
- ~/.profile
- ~/.config/hermes/env.sh
Limites que vale conhecer
- Só são restaurados os diretórios que estavam presentes no ambiente com que o Hermes foi lançado. O que o seu processo pai nunca teve não é inventado.
- Se você removeu de propósito um diretório do PATH dentro do seu profile, mas ele ainda estava no PATH de lançamento, ele volta. Esse é o trade-off pretendido: manter funcionando os comandos que já funcionavam.
- O Windows não é afetado — o PATH nativo é unido por ponto e vírgula e o bash vê uma cópia convertida.
- Ordem é precedência. Como as entradas restauradas entram no fim, os diretórios de sistema continuam antes dos seus diretórios customizados; não tente “consertar” problemas de PATH colocando um diretório gravável na frente.
Para uma checagem de ambiente mais ampla, rode hermes doctor. O comportamento de espaço de rascunho do terminal está em diretórios temporários do terminal, o layout de containers em modo container compartilhado, e a troca completa do shell em plugins de backend de terminal.
Status da release
A correção entrou na main em 29 de outubro de 2026 (PR #146080). A release atual, v0.22.5 (23 de outubro de 2026), não a inclui, então builds mais antigos ainda podem cair em command not found.
Bugs de PATH são desagradáveis justamente porque nunca levantam erro — alguns diretórios simplesmente somem. Pelo menos agora eles voltam para onde deveriam estar.