CodeGym /Corsi /ChatGPT Apps /Errori tipici di deploy e strategia di debug

Errori tipici di deploy e strategia di debug

ChatGPT Apps
Livello 7 , Lezione 4
Disponibile

1. Anatomia del deploy: dove può rompersi

È utile vedere prima l’intera catena. Il deploy di ChatGPT App nella tua architettura si può immaginare come una linea del genere:

flowchart TD
  A[Il tuo laptop
git commit] --> B[Repository Git
GitHub/GitLab] B --> C[Vercel Build
npm run build] C --> D[Vercel Deploy
Preview/Prod] D --> E[Endpoint HTTP
/mcp, /api/...] E --> F[ChatGPT / Dev Mode
tool calls, widget]

Un errore può comparire in uno qualsiasi di questi passaggi, ma i sintomi in ChatGPT sembrano più o meno gli stessi: "Error talking to app", "Network error" o semplicemente silenzio. Il tuo compito è non sparare alla cieca, ma capire prima: è caduto in fase di build, a runtime, oppure ChatGPT guarda l’URL sbagliato.

È comodo dividere i problemi in tre grandi categorie:

  • Errori di build: Vercel non è riuscito affatto a compilare il progetto. La produzione non si è aggiornata — è «positivo», ma vedi il build rosso.
  • Errori di runtime: la build è passata, ma alle richieste arrivano 500/502, timeout o comportamenti strani.
  • Config drift (deriva di configurazione): in locale tutto ok, su Vercel tutto ok nei log, ma ChatGPT punta a un URL vecchio, lavora con un manifesto vecchio o con variabili env vuote.

Percorreremo questi tre strati sviluppando in parallelo una strategia generale di debug.

2. Errori di build: quando il progetto non si compila

Questo è il primo tipo di problema dall’introduzione — errori di build: il progetto non si compila affatto perché Vercel non riesce a costruire correttamente il tuo progetto Next.js.

Node e Next.js: ambiente diverso, requisiti diversi

In locale potresti (purtroppo) usare una versione obsoleta di Node, mentre Vercel proverà a costruire il tuo progetto Next.js 16 con una versione di Node supportata (minimo 18.18.0). Se in package.json è specificata una versione incompatibile, la build può fallire in produzione, anche se il server di sviluppo locale partiva.

Un modo semplice per proteggersi — in package.json specificare esplicitamente "engines":

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

Così sia in locale sia in CI/su Vercel vedrai in anticipo che la versione di Node è troppo vecchia.

«Da me funziona!» e dipendenze dimenticate

Classico: hai installato una libreria con npm install some-lib, ma non hai committato il package-lock.json aggiornato o addirittura hai parte delle dipendenze installate globalmente. Su Vercel l’applicazione si compila «da zero», esegue npm install in base al manifesto, e la tua amata some-lib lì non c’è — ottieni un errore di build.

Qui aiuta una disciplina ferrea:

  • ogni nuova dipendenza va aggiunta e subito committata;
  • prima del push su main/production esegui npm run build in locale. Se la build locale cade, su Vercel andrà solo peggio.

File system case‑sensitive

In locale molti usano macOS o Windows, dove il file system di default non distingue il maiuscolo/minuscolo nel nome file. Su Vercel la build avviene in ambiente Linux, lì Widget.tsx e widget.tsx sono file diversi.

Bug tipico:

// Import nel codice
import { AppWidget } from "@/components/Widget";

// Ma nel repository il file è components/widget.tsx

Sul tuo computer tutto funziona, su Vercel — errore del modulo «Cannot find module '@/components/Widget'». Si risolve facendo ordine nei nomi e prestando attenzione al case.

Variabili env in fase di build

Un’altra fonte di sorprese — l’uso di process.env.* nel codice eseguito in fase di build (per esempio in next.config.mjs o in moduli importati durante la build). Se in locale hai caricato .env.local, ma su Vercel hai dimenticato di creare queste variabili per l’ambiente di build, la build o cadrà oppure — peggio — passerà con undefined e «incorporerà» valori non validi nel bundle.

Per ChatGPT App è particolarmente critico se, ad esempio, costruisci il baseURL per l’endpoint MCP o gli URL delle API esterne direttamente in fase di build.

Buona pratica — validare esplicitamente le variabili env critiche prima dell’avvio dell’applicazione (ne parleremo in una sezione separata), in modo che la build fallisca in modo chiaro e prevedibile.

3. Errori di runtime: quando tutto si compila, ma non funziona

Passiamo ora al secondo strato dall’introduzione — errori di runtime: la build è passata, ma in esecuzione tutto si rompe.

La build è passata, Vercel ha mostrato contento un deploy verde, hai puntato ChatGPT App all’URL di produzione — e in chat hai ottenuto "Error talking to app". Significa che i problemi sono saliti al livello di esecuzione.

Variabili env nulle o vuote

Molto spesso un incidente in produzione nel mondo ChatGPT App inizia con la parola undefined. In locale hai un curato .env.local con OPENAI_API_KEY, MCP_BASE_URL e altro, ma su Vercel hai dimenticato di creare queste variabili o hai confuso i nomi.

Per esempio stai leggendo:

const apiKey = process.env.OPENAI_API_KEY;

e su Vercel hai impostato OPENAI_APIKEY o OPENAI_API_KEY_PROD. Di conseguenza, alla prima invocazione dello strumento MCP il tuo route handler cade con un errore di autenticazione.

È molto meglio quando l’applicazione cade subito e in modo chiaro. Un buon pattern — un modulo separato nel tuo progetto Next.js che valida le variabili env al momento dell’import:

// 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"),
};

Ora, se hai dimenticato di impostare le variabili su Vercel, Next.js cadrà al primo import di env, e nei log ci sarà un messaggio leggibile "Missing required env var: ...".

È importante ricordare che su Vercel le modifiche alle variabili env non vengono prese automaticamente. Dopo aver cambiato i valori bisogna fare un nuovo deploy (redeploy), altrimenti il runtime continuerà a usare i valori vecchi.

Errori nei route handler e nell’endpoint MCP

Nel template ufficiale di ChatGPT App il server MCP è di solito implementato come app/mcp/route.ts. Dentro hai il codice che fa il parsing della richiesta JSON‑RPC, la inoltra allo strumento e restituisce la risposta. Se da qualche parte nella catena avviene un throw non gestito — all’utente in ChatGPT arriverà un 500.

Conviene sempre avvolgere il livello superiore dell’handler MCP in try/catch, loggare l’errore e restituire una risposta strutturata:

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

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

export async function POST(req: NextRequest) {
  try {
    const body = await req.json();
    // qui l'elaborazione della richiesta 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 }
    );
  }
}

Due note:

  • dynamic = "force-dynamic" aiuta a evitare generazione statica e caching inattesi per le route MCP in Next.js 16.
  • maxDuration = 30 indica esplicitamente a Vercel che il route handler può lavorare fino a 30 secondi, cosa importante per richieste LLM lunghe.

Timeout e «Network error» in ChatGPT

Vercel limita il tempo di esecuzione delle funzioni serverless: sui piani gratuiti è di solito circa 10 secondi, sui piani a pagamento può essere maggiore (fino a qualche minuto). Se il tuo strumento MCP fa una richiesta lunga al database o a un’API esterna, potrebbe non riuscire a rispondere in tempo, e ChatGPT riceverà "Network error" o uno stream interrotto.

Se usi lo streaming (SSE) per risultati parziali, è particolarmente importante inviare i primi byte della risposta prima della scadenza del timeout. Così la trasmissione può durare di più, ma la piattaforma non considererà la funzione «bloccata».

Trucchetto: misura il tempo delle invocazioni degli strumenti e loggalo insieme al nome dello strumento. Nei log si vedrà che, per esempio, search_flights impiega stabilmente 12 secondi e non rientra di poco nel 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;
  }
}

Poi invece di handler(args) chiami safeToolCall("search_flights", handler, args).

Rete e servizi esterni

A volte è tutta colpa di un banale https:// invece di http:// o di un baseURL obsoleto. Soprattutto se prima hai testato in locale con un URL, mentre in produzione hai un altro dominio o un’altra porta.

È utile estrarre gli URL di base nella configurazione (dipendente dall’ambiente) e non cucirli direttamente nel codice dello strumento. Così al cambio ambiente modifichi una sola variabile env, invece di ricordarti in quali cinque punti del codice avevi http://localhost:3001.

4. Configurazione e drift degli ambienti

E, infine, il terzo tipo nella nostra schema — il drift di configurazione tra gli ambienti.

Anche se la build è passata e a runtime i log sembrano sani, ChatGPT può comportarsi «come se girasse un’altra versione dell’app». È proprio il caso in cui il problema non è tanto nel codice quanto nella configurazione e nella coerenza degli ambienti.

Dev Mode vs produzione

In Dev Mode ChatGPT guarda l’URL del Connector che hai indicato manualmente: di solito è un URL di tunnel (https://myapp-dev.ngrok-free.app/mcp o simile) o un URL di staging su Vercel. In produzione (tramite Store o impostazioni dell’organizzazione) l’App deve puntare a un endpoint di produzione stabile, per esempio https://myapp.vercel.app/mcp.

Errore che fanno quasi tutti: hai deployato su Vercel, ma nelle impostazioni di ChatGPT App è ancora indicato l’URL del vecchio tunnel. Il server locale è spento, il tunnel è morto da tempo, e ChatGPT ci bussa diligentemente ottenendo 502. Nell’interfaccia appare "Error talking to app", e lo studente inizia a sistemare il codice dell’MCP che in realtà non viene nemmeno eseguito.

Si cura con la disciplina: dopo qualsiasi cambio di ambiente (tunnel → staging, staging → prod) controlla quale URL è impostato nel Dev Mode e nella configurazione di produzione dell’App.

Manifest vecchio e cache di ChatGPT

ChatGPT metterà in cache le informazioni sulla tua App: elenco degli strumenti, descrizioni, metadati. Quindi la situazione «ho cambiato lo schema dello strumento, ma il modello crede ancora che l’argomento si chiami come prima» è reale.

Per cambiamenti importanti degli strumenti è utile:

  • assicurarti di aver davvero deployato la nuova versione (controlla l’hash del commit nei log, stampalo nel log di avvio);
  • ricreare o ricollegare l’App in Dev Mode per costringere la piattaforma a rileggere il manifest;
  • durante il debug usare MCP Inspector, dove vedi esattamente l’elenco e gli schemi degli strumenti aggiornati.

Config delle env: dev/staging/prod

Abbiamo già parlato di come le variabili env possano far cadere build e runtime. Qui — uno sguardo dall’alto su dev/staging/prod e la coerenza dei valori tra loro.

Dolore comune: il tuo .env.local è perfetto, ma negli ambienti Vercel — regna il caos. Di conseguenza:

  • in locale hai una chiave API e un URL di servizio esterno;
  • su staging — valori completamente diversi;
  • su prod — metà delle variabili non è impostata.

Aiuta molto un semplice file di testo docs/env.md nel repository, dove elenchi: quali variabili servono, in quali ambienti sono obbligatorie, quali valori di esempio. Può sembrare burocrazia, ma al momento dell’incidente una lista del genere fa risparmiare ore.

5. Come appaiono gli errori dal lato ChatGPT

Guardiamo ora la situazione con gli occhi dell’utente di ChatGPT. Vede solo l’interfaccia e non sa nulla di Vercel, Node e MCP. E tu, purtroppo, per ora non sai nemmeno tu cosa esattamente si è rotto.

Sintomi tipici in ChatGPT:

  • messaggio "Error talking to [App Name]" subito dopo il tentativo di utilizzo;
  • spinner infinito senza errore visibile;
  • testo rosso "I encountered an error while running the tool";
  • il widget non appare o appare vuoto.

Ognuno di questi sintomi di solito corrisponde a un certo livello di guasto:

  • se l’App non è proprio raggiungibile (URL sbagliato, tunnel caduto, errore SSL), ChatGPT non riesce a raggiungere il tuo endpoint MCP — verifica la raggiungibilità del dominio nel browser e i log di Vercel con codici 4xx/5xx;
  • se l’MCP risponde con un JSON‑RPC valido con il campo error, ChatGPT scrive che lo strumento ha restituito un errore — è già una questione di logica di business o di validazione degli argomenti;
  • se l’MCP risponde con successo, ma l’HTML del widget è rotto o c’è un errore JS, nella console del widget (DevTools → iframe del widget) si vedrà che cosa è caduto.

Quindi buona abitudine: appena vedi un comportamento strano in chat, prendi subito il timestamp (al minuto) e vai nei log di Vercel a cercare le richieste in quel momento.

6. Strategia di debug: niente panico, agisci

Ora componiamo da tutto quanto detto un piccolo «playbook» — uno scenario d’azione quando qualcosa va storto. Obiettivo — sostituire la corsa in tondo con un algoritmo tranquillo.

Passo 1: determinare il tipo di problema

Se la build su Vercel è rossa — c’è da rallegrarsi: l’errore è stato catturato prima della produzione. Apri i log della build, guarda il primo errore reale (non 200 righe di warning) e riproduci in locale con il comando npm run build.

Se la build è verde e ChatGPT si lamenta — è runtime o configurazione. Controlla:

  • se l’URL di produzione della tua App è raggiungibile dal browser (https://myapp.vercel.app/mcp restituisce qualcosa);
  • se l’endpoint MCP restituisce 200/500 o non risolve affatto;
  • se l’URL nelle impostazioni dell’App coincide con quello che hai appena verificato.

Passo 2: leggere i log, non i pensieri

Tappa successiva — i log di Vercel: log server per il deploy e l’ambiente giusti (Preview/Production).

Cerca:

  • errori Error: Missing required env var ... — allora il problema è nella configurazione;
  • stack trace dall’handler MCP — allora cade la logica di business o il parsing dei dati in ingresso;
  • messaggi su timeout o superamento della durata della funzione.

In parallelo non dimenticare MCP Inspector. Se ti connetti allo stesso endpoint MCP tramite l’inspector e invochi manualmente gli strumenti, capirai rapidamente se il problema è nell’MCP stesso o nel collegamento ChatGPT ↔ MCP.

Passo 3: rollback rapido o hotfix?

Se vedi che il deploy di produzione è chiaramente rotto (per esempio, la route MCP lancia sempre lo stesso errore a ogni richiesta), e il deploy precedente era sano, la soluzione giusta è fare rollback. Vercel permette di passare rapidamente al deploy riuscito precedente senza ricostruire — è in sostanza un cambio di versione attiva.

È meglio che tentare di sistemare la produzione «al volo», soprattutto se non capisci ancora a fondo la causa dell’incidente.

Quando la situazione è stabilizzata, analizzi con calma la causa, scrivi i test, correggi nel codice e solo dopo rilasci la versione successiva.

Passo 4: fissare le lezioni nella documentazione

Ogni incidente serio — è un motivo per aggiornare il README interno:

  • aggiungere all’elenco una variabile env obbligatoria senza la quale tutto cade;
  • fissare quale caso specifico ha portato all’errore (per esempio, «import con case errato nei nomi file»);
  • descrivere un breve algoritmo di azioni che ha aiutato a sistemare rapidamente tutto.

Sembra noioso, ma tra un paio di mesi ringrazierai te stesso.

7. Piccoli accorgimenti pratici nel codice

Ora prendiamo alcuni passi del nostro playbook e fissiamoli con piccoli accorgimenti di codice nella nostra app didattica (ChatGPT App).

Modulo di configurazione unico

Abbiamo già scritto un semplice validatore di variabili env. Si può estendere per distinguere gli ambienti:

// 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",
};

Un modulo così evidenzierà subito se la produzione è partita senza la variabile necessaria.

Logging delle richieste MCP in ingresso

Semplice ma molto utile wrapper per l’handler MCP:

// app/lib/mcp-logger.ts
export function logMcpRequest(body: unknown) {
  console.log("[mcp] request", {
    time: new Date().toISOString(),
    // non registriamo dati sensibili
    keys: typeof body === "object" && body !== null
      ? Object.keys(body as Record<string, unknown>)
      : typeof body,
  });
}

E usiamolo in 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 });
  }
}

Nei log vedrai che cosa arriva da ChatGPT: almeno per le chiavi ("jsonrpc", "method", "params"), e capirai più facilmente quale invocazione cade.

Semplice healthcheck dell’endpoint MCP

A volte è utile avere un piccolo route handler tipo «healthcheck» per il server MCP, che ChatGPT non chiama direttamente, ma che puoi aprire al volo nel browser per capire se il server è vivo e se vede le sue variabili env:

// 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 risponde con status: "ok", allora almeno la pipeline di base fino al tuo codice Node è viva.

8. Errori tipici nel deploy e nel debug

Errore n. 1: Deploy senza npm run build locale.
Quando uno sviluppatore non esegue mai la build in locale, scopre la versione incompatibile di Node, il problema con i path o l’errore TS solo su Vercel. Questo allunga il ciclo «rotto → sistemato», perché ogni esperimento è un nuovo deploy. L’abitudine di lanciare npm run build prima del push su main fa risparmiare molto tempo (vedi anche sezione 2 e passo 6.1 sul npm run build locale).

Errore n. 2: I segreti sono rimasti solo in .env.local.
Il progetto funziona perfettamente sulla macchina dell’autore, ma in produzione cade perché process.env.OPENAI_API_KEY === undefined. La causa è banale: le variabili env sono state dimenticate nelle impostazioni di Vercel (e talvolta hanno anche un altro nome). Si dimentica spesso la separazione Development/Preview/Production e ci si stupisce che staging e prod si comportino diversamente (dettagli — sezioni 3.1, 4.3 e 7.1).

Errore n. 3: Usare NEXT_PUBLIC_* per i segreti.
In Next.js tutte le variabili con prefisso NEXT_PUBLIC_ finiscono nel bundle del browser. Se per distrazione hai chiamato una chiave API NEXT_PUBLIC_OPENAI_API_KEY, verrà inviata al browser dell’utente e potrà essere estratta dagli strumenti di sviluppo. Non si deve fare. Pubblici devono essere solo valori sicuri (per esempio, identificatori di feature flag, non token).

Errore n. 4: Ignorare i log di Vercel e tentare di «sistemare tramite ChatGPT».
A volte uno sviluppatore vede in chat "Error talking to app" e passa ore a cambiare prompt, descrizioni degli strumenti, sistemare qualcosa nel Dev Mode, ma non guarda mai i log serverless. E lì c’è un errore chiarissimo: "Missing env var", "Cannot find module" o lo stack trace di uno strumento specifico. Un buon ingegnere guarda prima i log e poi discute con il modello.

Errore n. 5: Confusione tra Dev Mode e App di produzione.
Dopo il primo deploy riuscito su Vercel è facile dimenticare che il Dev Mode può ancora puntare al vecchio tunnel o a un URL di preview. Di conseguenza sei convinto di testare la versione di produzione, ma in realtà stai parlando con un branch locale che andrebbe eliminato da tempo. Oppure il contrario: pensi di testare modifiche di bozza, ma ChatGPT bussa all’endpoint di produzione. Bisogna controllare regolarmente quale URL è indicato nelle impostazioni dell’App e del Dev Mode (vedi anche sezione 4.1 su Dev Mode e produzione).

Errore n. 6: Aspettarsi che la modifica di una variabile env su Vercel funzioni «al volo».
Alcuni studenti cambiano i valori delle variabili nel pannello di Vercel e corrono subito in ChatGPT a verificare il risultato. Ma il runtime usa ancora i valori vecchi, perché non c’è stato un redeploy. Qualsiasi modifica alle variabili env richiede un nuovo deploy, altrimenti la funzione non vedrà l’aggiornamento (dettagli — sezione 3.1).

Errore n. 7: Mancanza di una semplice strategia di rollback.
Nel momento dell’incidente è forte la tentazione di «pushare al volo un fix» direttamente su main. Ma così aggiungi un altro deploy potenzialmente rotto, mentre gli utenti soffrono. Molto più tranquillo avere l’abitudine: in caso di errore grave fai subito rollback al deploy riuscito precedente, sistemi il problema in un branch separato e solo dopo rilasci la nuova versione. Vercel offre un’interfaccia comoda per questo, sarebbe un peccato non usarla.

1
Sondaggio/quiz
Debug e deploy, livello 7, lezione 4
Non disponibile
Debug e deploy
Ambienti, debug e deploy (Vercel + tunnel)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION