CodeGym /Cursos /ChatGPT Apps /Erros típicos de deploy e estratégia de depuração

Erros típicos de deploy e estratégia de depuração

ChatGPT Apps
Nível 7 , Lição 4
Disponível

1. Anatomia do deploy: onde pode quebrar

É útil primeiro ver toda a cadeia. O deploy do ChatGPT App na sua arquitetura pode ser mentalmente expandido nesta linha:

flowchart TD
  A[Seu laptop
git commit] --> B[Repositório Git
GitHub/GitLab] B --> C[Vercel Build
npm run build] C --> D[Vercel Deploy
Preview/Prod] D --> E[HTTP endpoint
/mcp, /api/...] E --> F[ChatGPT / Dev Mode
tool calls, widgets]

O erro pode aparecer em qualquer uma dessas etapas, mas os sintomas no ChatGPT parecem aproximadamente iguais: "Error talking to app", "Network error" ou simplesmente silêncio. Seu objetivo — não atirar no escuro, mas primeiro entender: isso caiu na etapa de build, durante a execução, ou o ChatGPT está apontando para o lugar errado.

É útil dividir os problemas em três grandes categorias:

  • Erros de build: a Vercel não conseguiu compilar o projeto. A produção não foi atualizada — isso é “bom”, mas você vê um build vermelho.
  • Erros de runtime: o build passou, mas nas requisições vêm 500/502, timeouts ou comportamentos estranhos.
  • Config drift (deriva de configuração): localmente está tudo ok, na Vercel está tudo ok pelos logs, mas o ChatGPT usa uma URL antiga, trabalha com um manifesto antigo ou com variáveis de ambiente vazias.

Vamos percorrer essas três camadas e, em paralelo, desenvolver uma estratégia geral de depuração.

2. Erros de build: quando o projeto não compila

Este é o primeiro tipo de problema da introdução — erros de build: o projeto não compila porque a Vercel não consegue construir seu projeto Next.js.

Node e Next.js: outro ambiente, outros requisitos

Localmente você pode (infelizmente) usar um Node desatualizado, enquanto a Vercel tentará construir seu projeto Next.js 16 com uma versão suportada do Node (mínimo 18.18.0). Se no package.json estiver especificada explicitamente uma versão incompatível, a compilação pode falhar em produção, embora o servidor de desenvolvimento rodasse aí.

Uma forma simples de se proteger é indicar no package.json o campo "engines":

{
  "engines": {
    "node": ">=18.18.0"
  }
}

Assim, tanto localmente quanto no CI/na Vercel você verá antecipadamente que o Node é muito antigo.

“Funciona na minha máquina!” e dependências esquecidas

Clássico: você instalou uma biblioteca com npm install some-lib, mas não fez commit do package-lock.json atualizado ou, pior, tem partes das dependências instaladas globalmente. Na Vercel o aplicativo é construído “do zero”, ela executa npm install com base no manifesto, mas a sua amada some-lib não está lá — resultado: erro de build.

Disciplina rígida ajuda aqui:

  • quaisquer novas dependências são adicionadas e comitadas imediatamente;
  • antes de dar push no main/production você roda npm run build localmente. Se o build local falhar, na Vercel só vai piorar.

Sistema de arquivos case‑sensitive

Localmente, muitos usam macOS ou Windows, onde o sistema de arquivos por padrão não diferencia maiúsculas/minúsculas no nome do arquivo. Na Vercel, a compilação ocorre em um ambiente Linux, lá Widget.tsx e widget.tsx são arquivos diferentes.

Bug típico:

// Import no código
import { AppWidget } from "@/components/Widget";

// Mas no repositório o arquivo é components/widget.tsx

No seu computador tudo funciona; na Vercel — erro de módulo “Cannot find module '@/components/Widget'”. Resolve-se organizando os nomes e sendo cuidadoso com o uso de maiúsculas/minúsculas.

Variáveis de ambiente na etapa de build

Outra fonte de surpresas — usar process.env.* em código que roda na etapa de build (por exemplo, em next.config.mjs ou em módulos importados durante o build). Se localmente você carrega .env.local, mas na Vercel esquece de definir essas variáveis para o ambiente de build, a compilação ou vai falhar, ou — pior — vai passar com undefined e “assar” valores inválidos no bundle.

Para o ChatGPT App isso é especialmente crítico se, por exemplo, você forma o baseURL para o endpoint MCP ou URLs de APIs externas já na etapa de build.

Uma boa prática é validar explicitamente variáveis de ambiente críticas antes mesmo de iniciar o aplicativo (falaremos disso em uma seção separada), para que o build falhe de forma ruidosa e previsível.

3. Erros de runtime: quando tudo compilou, mas não funciona

Agora passamos à segunda camada da introdução — erros de runtime: o build passou, mas durante a execução tudo quebra.

A compilação passou, a Vercel exibiu com alegria um deploy verde, você alternou o ChatGPT App para a URL de produção — e recebeu no chat "Error talking to app". Ou seja, os problemas subiram para o nível de execução.

Variáveis de ambiente nulas ou vazias

Com frequência o incidente em produção no mundo do ChatGPT App começa com a palavra undefined. Localmente você tem um .env.local caprichado com OPENAI_API_KEY, MCP_BASE_URL e outros, mas na Vercel você esqueceu de definir essas variáveis ou confundiu os nomes.

Por exemplo, você lê:

const apiKey = process.env.OPENAI_API_KEY;

e na Vercel definiu OPENAI_APIKEY ou OPENAI_API_KEY_PROD. Como resultado, na primeira chamada do instrumento MCP seu route handler cai com erro de autenticação.

É muito melhor quando o aplicativo cai logo e de forma clara. Um bom padrão é ter um módulo separado no seu projeto Next.js que valida as variáveis de ambiente ao importar:

// app/lib/env.ts
const required = ["OPENAI_API_KEY", "MCP_BASE_URL"] as const;

type RequiredKey = (typeof required)[number];

function getEnv(key: RequiredKey): string {
  const value = process.env[key];
  if (!value) {
    throw new Error(`Missing required env var: ${key}`);
  }
  return value;
}

export const env = {
  OPENAI_API_KEY: getEnv("OPENAI_API_KEY"),
  MCP_BASE_URL: getEnv("MCP_BASE_URL"),
};

Agora, se você esqueceu de configurar as variáveis na Vercel, o Next.js vai cair logo na primeira importação de env, e nos logs haverá uma mensagem humana "Missing required env var: ...".

É importante lembrar que na Vercel as mudanças nas variáveis de ambiente não são aplicadas automaticamente. Depois de alterar valores, é preciso fazer um novo deploy (redeploy), caso contrário o runtime continuará com os valores antigos.

Erros em route handlers e no endpoint MCP

No template oficial do ChatGPT App, o servidor MCP normalmente é implementado em app/mcp/route.ts. Dentro, há código que faz o parse do pedido JSON‑RPC, roteia para o instrumento e retorna a resposta. Se em algum ponto da cadeia ocorre um throw sem tratamento — o usuário no ChatGPT receberá 500.

Vale sempre envolver o nível superior do handler MCP em try/catch, logar o erro e retornar uma resposta estruturada:

// app/mcp/route.ts
import { NextRequest, NextResponse } from "next/server";

export const dynamic = "force-dynamic";
export const maxDuration = 30; // segundos

export async function POST(req: NextRequest) {
  try {
    const body = await req.json();
    // aqui vai o processamento do pedido MCP
    const result = await handleMcpRequest(body);
    return NextResponse.json(result);
  } catch (error) {
    console.error("MCP route error", error);
    return NextResponse.json(
      { error: "Internal MCP error" },
      { status: 500 }
    );
  }
}

Alguns pontos:

  • dynamic = "force-dynamic" ajuda a evitar geração estática inesperada e cache para rotas MCP no Next.js 16.
  • maxDuration = 30 diz explicitamente à Vercel que o route handler pode rodar por até 30 segundos, importante em requisições LLM longas.

Timeouts e “Network error” no ChatGPT

A Vercel limita o tempo de execução de funções serverless: nos planos gratuitos isso costuma ser por volta de 10 segundos, nos pagos pode ser maior (até alguns minutos). Se seu instrumento MCP faz uma requisição longa a um banco de dados ou API externa, talvez não dê tempo de responder, e o ChatGPT receberá "Network error" ou um stream interrompido.

Se você usa streaming (SSE) para resultados parciais, é especialmente importante enviar os primeiros bytes da resposta antes de expirar o timeout. Assim, a transmissão pode continuar por mais tempo, mas a plataforma não considerará a função “travada”.

Dica rápida: meça o tempo de chamada dos instrumentos e registre-o junto com o nome do instrumento. Nos logs ficará visível que, por exemplo, search_flights ocupa consistentemente 12 segundos e está só um pouco acima do limite.

export async function safeToolCall<TInput, TOutput>(
  name: string,
  handler: (input: TInput) => Promise<TOutput>,
  input: TInput
): Promise<TOutput> {
  const started = Date.now();
  try {
    const result = await handler(input);
    console.log("[tool] ok", name, { ms: Date.now() - started });
    return result;
  } catch (error) {
    console.error("[tool] fail", name, {
      ms: Date.now() - started,
      error,
    });
    throw error;
  }
}

Depois, em vez de handler(args), você chama safeToolCall("search_flights", handler, args).

Rede e serviços externos

Às vezes é algo tão simples quanto https:// em vez de http:// ou um baseURL desatualizado. Especialmente se você primeiro testou na máquina local com uma URL e em produção já tem outro domínio ou outra porta.

É útil extrair as URLs base para uma configuração (dependente do ambiente) e não fixá-las diretamente no código do instrumento. Assim, ao trocar de ambiente você muda uma variável de ambiente, e não lembra em quais cinco lugares no código você tinha http://localhost:3001.

4. Configuração e deriva entre ambientes

E, por fim, o terceiro tipo da nossa esquemática — a deriva de configuração entre ambientes.

Mesmo que o build tenha passado e o runtime pareça saudável pelos logs, o ChatGPT pode se comportar “como se estivesse rodando outra versão do app”. Este é o caso em que o problema não está tanto no código, mas na configuração e na consistência entre ambientes.

Dev Mode versus produção

No Dev Mode o ChatGPT usa o Connector URL que você indicou manualmente: geralmente é uma URL de túnel (https://myapp-dev.ngrok-free.app/mcp ou algo parecido) ou uma URL de staging na Vercel. Em produção (via Store ou configurações da organização) o App deve apontar para um endpoint de produção estável, por exemplo https://myapp.vercel.app/mcp.

Erro que quase todo mundo comete: você fez o deploy na Vercel, mas nas configurações do ChatGPT App ainda está a URL antiga do túnel. O servidor local está desligado, o túnel morreu faz tempo, e o ChatGPT tenta acessá-lo e recebe 502. Na interface isso aparece como "Error talking to app", e o aluno começa a consertar o código do MCP que nem está sendo executado.

Resolve-se com disciplina: após qualquer mudança de ambiente (túnel → staging, staging → prod), verifique qual URL está configurada no Dev Mode e na configuração de produção do App.

Manifesto antigo e cache do ChatGPT

O ChatGPT vai fazer cache das informações do seu App: lista de instrumentos, descrições, metadados. Portanto, a situação “eu mudei o schema do instrumento, mas o modelo ainda acha que o argumento se chama do jeito antigo” — acontece.

Para mudanças significativas nos instrumentos, é útil:

  • garantir que você realmente fez deploy da nova versão (ver o hash do commit nos logs, exibi-lo no log de inicialização);
  • recriar ou reconectar o App no Dev Mode para forçar a plataforma a reler o manifesto;
  • durante a depuração, trabalhar via MCP Inspector, onde você vê com certeza a lista e os schemas atuais dos instrumentos.

Config de env: dev/staging/prod

Já falamos como variáveis de ambiente podem derrubar build e runtime. Aqui — uma visão de cima sobre dev/staging/prod e a consistência de valores entre eles.

Dor comum: seu .env.local é perfeito, e nos ambientes da Vercel — um zoológico. No fim:

  • localmente você tem uma chave de API e uma URL de serviço externo;
  • no staging — valores completamente diferentes;
  • no prod — metade das variáveis não está definida.

Ajuda muito um arquivo de texto simples docs/env.md no repositório, onde você lista: quais variáveis são necessárias, em quais ambientes são obrigatórias e exemplos de valores. Pode parecer burocracia, mas no momento de um incidente essa lista economiza horas.

5. Como os erros aparecem no lado do ChatGPT

Agora vamos olhar a situação pelos olhos do usuário do ChatGPT. Ele vê apenas a interface e não sabe nada sobre Vercel, Node e MCP. E você, infelizmente, por enquanto também não sabe o que exatamente quebrou.

Sintomas típicos no ChatGPT:

  • a mensagem "Error talking to [App Name]" imediatamente após tentar usar;
  • um spinner infinito sem erro visível;
  • texto vermelho "I encountered an error while running the tool";
  • o widget não aparece ou aparece vazio.

Cada um desses sintomas geralmente corresponde a um nível específico de falha:

  • se o App estiver totalmente indisponível (URL errada, túnel caiu, erro de SSL), o ChatGPT não consegue alcançar seu endpoint MCP — verifique a disponibilidade do domínio no navegador e os logs da Vercel com códigos 4xx/5xx;
  • se o MCP responder com JSON‑RPC válido com o campo error, o ChatGPT informa que o instrumento retornou erro — isso já é assunto de lógica de negócio ou validação de argumentos;
  • se o MCP responder com sucesso, mas a resposta contiver HTML de widget quebrado ou erro de JS, então no console do widget (DevTools → iframe do widget) será possível ver o que exatamente caiu.

Por isso, um bom hábito: assim que vir um comportamento estranho no chat, anote o timestamp (até o minuto) e vá aos logs da Vercel procurar as requisições nesse horário.

6. Estratégia de depuração: como não entrar em pânico e agir

Agora vamos montar a partir de tudo um pequeno “playbook” — um roteiro de ações quando algo deu errado. A meta — trocar a corrida em círculos por um algoritmo tranquilo.

Etapa 1: identificar o tipo de problema

Se o build na Vercel está vermelho — comemore: o erro foi pego antes da produção. Abra os logs do build, procure o primeiro erro real (e não 200 linhas de avisos) e reproduza localmente com npm run build.

Se o build está verde e o ChatGPT reclama — é runtime ou configuração. Verifique:

  • se a URL de produção do seu App está acessível no navegador (https://myapp.vercel.app/mcp retorna algo?);
  • se o endpoint MCP retorna 200/500 ou nem resolve;
  • se a URL nas configurações do App coincide com a que você acabou de testar.

Etapa 2: leia os logs, não adivinhe

Próxima parada — logs da Vercel: logs do servidor para o deploy e ambiente corretos (Preview/Production).

Procure por:

  • erros Error: Missing required env var ... — problema de configuração;
  • stack trace do handler MCP — falha na lógica de negócio ou no parse dos dados de entrada;
  • mensagens de timeout ou estouro de duração da função.

Em paralelo, não esqueça do MCP Inspector. Se você se conectar ao mesmo endpoint MCP via inspector e chamar os instrumentos manualmente, logo ficará claro se o problema está no próprio MCP ou na ligação ChatGPT ↔ MCP.

Etapa 3: rollback rápido ou hotfix?

Se você vê que o deploy de produção está claramente quebrado (por exemplo, a rota MCP lança o mesmo erro em toda requisição), e o deploy anterior estava saudável, a decisão certa é reverter. A Vercel permite alternar rapidamente para um deploy anterior bem‑sucedido sem reconstruir — é basicamente trocar a versão ativa.

Isso é melhor do que tentar consertar a produção “ao vivo”, especialmente se você ainda não entende a causa do incidente.

Quando a situação estiver estabilizada, você analisa a causa com calma, escreve testes, corrige no código e só depois lança a próxima versão.

Etapa 4: consolide o aprendizado em documentação

Qualquer incidente sério — motivo para atualizar seu README interno:

  • adicionar à lista a variável de ambiente obrigatória sem a qual tudo cai;
  • registrar qual caso exatamente levou ao erro (por exemplo, “import com capitalização errada de nomes de arquivos”);
  • descrever um algoritmo curto de ações que ajudou a consertar tudo rapidamente.

Parece entediante, mas daqui a alguns meses você vai agradecer a si mesmo.

7. Pequenas práticas úteis no código

Agora vamos pegar alguns passos do nosso playbook e consolidá‑los com pequenas práticas de código no nosso app de estudo (ChatGPT App).

Módulo de configuração unificado

Já escrevemos um validador simples de variáveis de ambiente. Podemos incrementá‑lo para diferenciar ambientes:

// app/lib/config.ts
type NodeEnv = "development" | "test" | "production";

const nodeEnv = (process.env.NODE_ENV || "development") as NodeEnv;

const requiredBase = ["OPENAI_API_KEY"] as const;
const requiredProd = ["MCP_BASE_URL"] as const;

function ensure(keys: readonly string[]) {
  for (const key of keys) {
    if (!process.env[key]) {
      throw new Error(`Missing env var ${key} for NODE_ENV=${nodeEnv}`);
    }
  }
}

ensure(requiredBase);
if (nodeEnv === "production") {
  ensure(requiredProd);
}

export const config = {
  nodeEnv,
  openaiApiKey: process.env.OPENAI_API_KEY!,
  mcpBaseUrl: process.env.MCP_BASE_URL ?? "http://localhost:3000/mcp",
};

Um módulo assim destacará imediatamente se a produção foi iniciada sem a variável necessária.

Log de requisições MCP de entrada

Uma camada simples, mas muito útil, para o handler MCP:

// app/lib/mcp-logger.ts
export function logMcpRequest(body: unknown) {
  console.log("[mcp] request", {
    time: new Date().toISOString(),
    // não registramos dados sensíveis
    keys: typeof body === "object" && body !== null
      ? Object.keys(body as Record<string, unknown>)
      : typeof body,
  });
}

E usamos em app/mcp/route.ts:

import { logMcpRequest } from "@/app/lib/mcp-logger";

export async function POST(req: NextRequest) {
  try {
    const body = await req.json();
    logMcpRequest(body);
    const result = await handleMcpRequest(body);
    return NextResponse.json(result);
  } catch (error) {
    console.error("MCP route error", error);
    return NextResponse.json({ error: "Internal error" }, { status: 500 });
  }
}

Nos logs você verá o que chega do ChatGPT: pelo menos pelas chaves ("jsonrpc", "method", "params"), e ficará mais fácil entender qual chamada está caindo.

Verificação simples de disponibilidade do endpoint MCP

Às vezes é útil ter um pequeno route handler tipo “healthcheck” para o servidor MCP, que o ChatGPT não chama diretamente, mas que você pode abrir rapidamente no navegador para entender se o servidor está vivo e se vê suas variáveis de ambiente:

// app/api/health/route.ts
import { NextResponse } from "next/server";
import { config } from "@/app/lib/config";

export async function GET() {
  return NextResponse.json({
    status: "ok",
    env: config.nodeEnv,
    hasOpenAiKey: !!config.openaiApiKey,
  });
}

Se https://myapp.vercel.app/api/health responde status: "ok", então pelo menos o pipeline básico até seu código Node está vivo.

8. Erros comuns no deploy e na depuração

Erro nº 1: Deploy sem rodar npm run build localmente.
Quando o desenvolvedor nunca roda o build localmente, só descobre sobre a versão incompatível do Node, problema de paths ou erro de TS na Vercel. Isso alonga o ciclo “quebrou → consertou”, porque cada experimento é um novo deploy. O hábito de executar npm run build antes do push no main economiza muito tempo (veja também a seção 2 e o passo 6.1 sobre o npm run build local).

Erro nº 2: Segredos ficaram apenas no .env.local.
O projeto funciona perfeitamente na máquina do autor, mas em produção cai por causa de process.env.OPENAI_API_KEY === undefined. A razão é banal: esqueceram de adicionar as variáveis no painel da Vercel (e às vezes até deram outro nome). Com frequência esquecem a separação Development/Preview/Production e se surpreendem que staging e produção se comportam de formas diferentes (detalhes — seções 3.1, 4.3 e 7.1).

Erro nº 3: Uso de NEXT_PUBLIC_* para segredos.
No Next.js todas as variáveis com o prefixo NEXT_PUBLIC_ vão para o bundle do navegador. Se por descuido você nomear a chave de API como NEXT_PUBLIC_OPENAI_API_KEY, ela irá para o navegador do usuário e pode ser extraída via devtools. Não faça isso. Devem ser públicas apenas informações seguras (por exemplo, identificadores de feature flags, mas não tokens).

Erro nº 4: Ignorar os logs da Vercel e tentar “consertar via ChatGPT”.
Às vezes o desenvolvedor vê no chat "Error talking to app" e passa horas mudando prompts, descrições de instrumentos, ajustando algo no Dev Mode, mas não abre os logs do serverless uma única vez. E lá está um erro bem claro: "Missing env var", "Cannot find module" ou o stack trace de um instrumento específico. Um bom engenheiro primeiro olha os logs, depois discute com o modelo.

Erro nº 5: Confundir Dev Mode e o App de produção.
Após o primeiro deploy bem‑sucedido na Vercel é fácil esquecer que o Dev Mode ainda pode apontar para o túnel antigo ou para a URL de preview. Resultado: você tem certeza de que está testando a versão de produção, mas na verdade está falando com uma branch local que já deveria ter sido excluída. Ou o oposto: você acha que está testando rascunhos, mas o ChatGPT acessa o endpoint de produção. Verifique regularmente qual URL está indicada nas configurações do App e no Dev Mode (veja também a seção 4.1 sobre Dev Mode e produção).

Erro nº 6: Esperar que a mudança de variável de ambiente na Vercel funcione “ao vivo”.
Alguns alunos mudam valores de variáveis no painel da Vercel e correm para o ChatGPT para verificar o resultado. Mas o runtime ainda usa os valores antigos, porque não houve redeploy. Qualquer mudança em variáveis de ambiente exige um novo deploy, caso contrário a função não verá a atualização (detalhes — seção 3.1).

Erro nº 7: Falta de uma estratégia simples de rollback.
No momento do incidente é tentador “fazer um fix rápido” direto no main. Mas isso adiciona mais um deploy potencialmente quebrado, e os usuários sofrem nesse meio‑tempo. É muito mais tranquilo ter o hábito de: em erro sério, reverter imediatamente para o último deploy bem‑sucedido, corrigir o problema em uma branch separada e só depois lançar a nova versão. A Vercel oferece uma interface conveniente para isso — aproveite.

1
Pesquisa/teste
Debug & Deploy, nível 7, lição 4
Indisponível
Debug & Deploy
Environments, Debug & Deploy (Vercel + túnel)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION