Voltar as noticias
Meu Wrapper `claude -p` Captura Timeouts. Uma Saída Não Zero Não é um Timeout, Então Ele Apenas Falha.
MCP ProtocolMediaEN

Meu Wrapper `claude -p` Captura Timeouts. Uma Saída Não Zero Não é um Timeout, Então Ele Apenas Falha.

Dev.to - MCP·28 de julho de 2026

Dois dias atrás, publiquei um post sobre uma convenção de ERRO: que eu havia criado no código de chamada de IA deste repositório: cada caminho de falha de _claude() — o helper que meu servidor MCP e meu script de mensagem de commit usam para chamar claude -p — deveria retornar uma string começando com ERRO:, para que os chamadores pudessem fazer result.startswith("ERRO:") em vez de confiar na saída como uma mensagem de commit real. Esse post corrigiu o único lugar onde a convenção não estava realmente sendo seguida: o ramo de timeout retornava uma string simples sem prefixo.

Eu fechei aquele post tratando a convenção como concluída. Não estava. Há um terceiro caminho de falha na mesma função exata, e ele não retorna uma string — ele levanta uma exceção.

o código como estava

server.py's _claude(), após a correção de segunda-feira:

def _claude(prompt: str, system: str = None) -> str:
    full = (system + "\n\n" + prompt) if system else prompt
    try:
        raw = subprocess.check_output(["claude", "-p", full], text=True, timeout=20).strip()
    except subprocess.TimeoutExpired:
        return "ERRO: claude -p excedeu o tempo limite após 20s"
    return "\n".join(
        l for l in raw.splitlines()
        if not _STRIP_RE.search(l)
    ).strip()

Uma cláusula except. subprocess.check_output não apenas excede o tempo limite, no entanto — ele também levanta subprocess.CalledProcessError se o processo filho sair com um status diferente de zero, e FileNotFoundError se o binário não estiver no PATH de forma alguma. Nenhum desses é um TimeoutExpired. Nenhum é capturado. Ambos se propagam diretamente de _claude(), para fora da ferramenta generate_commit_message do MCP, e até o que quer que esteja segurando a chamada da ferramenta.

Eu estava tratando "a convenção tem dois caminhos de falha, certifique-se de que eles concordem" como o problema inteiro. Tem três, e o terceiro não é uma discordância sobre formatação — é a total ausência de qualquer string.

provando isso, não apenas lendo

Eu não queria confiar apenas na palavra do meu próprio código sobre o que subprocess.check_output faz sob uma falha real de claude -p, então eu construí uma. Um binário substituto de uma linha no PATH antes do verdadeiro claude:

#!/bin/sh
echo "falha simulada: taxa limitada" >&2
exit 1

Então eu chamei o caminho de diff em estágio real através de git_commit.py (mesma chamada de subprocesso, mesma except subprocess.TimeoutExpired lacuna) com um arquivo em estágio real:

$ python3 git_commit.py
falha simulada: taxa limitada
Rastreamento (última chamada mais recente):
  Arquivo "git_commit.py", linha 36, em <module>
    raw = subprocess.check_output(
          ...
subprocess.CalledProcessError: O comando '['claude', '-p', ...]' retornou status de saída diferente de zero 1.

Rastreamento completo, código de saída 1, nenhuma mensagem limpa em lugar nenhum. Eu fiz a mesma coisa diretamente no server.py's _claude() (substituindo a importação mcp para que eu pudesse carregar o módulo sem o pacote real instalado) e obtive a mesma exceção não capturada. Então eu esvaziei o PATH de qualquer coisa chamada claude completamente e obtive FileNotFoundError da mesma forma — mesma lacuna, gatilho diferente.

A versão do hook de commit disso é silenciosamente salva por um acidente, não por uma decisão de design: hooks/prepare-commit-msg executa git_commit.py com 2>/dev/null e só escreve no arquivo de mensagem de commit se a stdout capturada não estiver vazia:

MSG="$(python "$SCRIPT" 2>/dev/null)" && [ -n "$MSG" ] && printf '%s\n' "$MSG" > "$1"

O rastreamento vai para stderr, que é descartado; stdout está vazio; o hook volta ao template padrão do git. Esse é o resultado certo, mas é o resultado certo pela razão errada — funciona porque o hook acaba descartando stderr, não porque git_commit.py lida com a falha. Execute o script diretamente, fora do hook, e você obtém um rastreamento Python bruto onde uma mensagem de "claude falhou, aqui está o porquê" deveria estar. A ferramenta MCP não tem um wrapper que descarta stderr — uma exceção não capturada lá é apenas uma exceção não capturada.

a correção

Mesma forma que o ramo de timeout, estendida para cobrir as outras duas maneiras subprocess.check_output...

Contexto Triplo Up

A implementação correta de protocolos de comunicação com agentes de IA é crucial para evitar falhas em sistemas automatizados. Empresas brasileiras que utilizam ferramentas de IA devem garantir que suas integrações tratem adequadamente erros e exceções para manter a confiabilidade dos serviços.

Noticias relacionadas

Gostou do conteudo?

Receba toda semana as principais novidades sobre WebMCP.