analista: il motivo di un guasto si prende da stdout E da stderr, o si perde
Il 2026-08-28 alle 00:37:00Z la CLI `claude` e' uscita 1 scrivendo «Failed to authenticate: OAuth session expired and could not be refreshed» su STDOUT, con stderr VUOTO. `interroga()` componeva il motivo dal solo stderr, quindi nel DB e' finito `analisi_stato='errore'`, `analisi_motivi='uscita 1: '` — e su Telegram e' partito «analisi non disponibile (errore): uscita 1:». La meccanica ha retto (nessuna analisi di ieri spacciata per quella di oggi, esito registrato, notifica partita): a mancare era solo il PERCHE'. P4 — un'allerta risponde a due domande, e con la prima sola la causa si ricostruisce aprendo a mano il transcript della sessione headless. P3 — quando la diagnosi serve, il guasto e' gia' rientrato: il motivo si cattura li' o mai. - `motivo_uscita(returncode, stdout, stderr)`: unisce i due canali (stderr per primo), tronca a 300 caratteri. - Silenzio totale su entrambi i canali -> lo DICE, invece di lasciare i due punti a vuoto: «la CLI non ha detto perche'» e «il perche' l'abbiamo perso noi» sono guasti diversi e finora si scrivevano uguali. - 4 test nuovi, fra cui la regressione col messaggio testuale del 28/08. NON risolve la causa a monte: perche' quella refresh sia fallita non sta in nessun log locale. Il refresh token era valido (rinnovo automatico riuscito alle 08:03:35Z dello stesso giorno, scadenza 2026-09-25) e la stessa chiamata rifatta a mano esce 0 -> guasto transitorio, unico su 24 sessioni locali. Da qui in avanti, se ricapita, il motivo si legge dalla notifica. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
-1
@@ -149,6 +149,23 @@ def costruisci_prompt(pagina: str, storico: str) -> str:
|
||||
f"Scrivi ora l'analisi, e nient'altro.")
|
||||
|
||||
|
||||
def motivo_uscita(returncode: int, stdout: str | None, stderr: str | None) -> str:
|
||||
"""Il motivo di un'uscita non-zero della CLI, preso da stderr E da stdout.
|
||||
|
||||
PERCHE' ENTRAMBI. Il 2026-08-28 alle 00:37Z la CLI `claude` e' uscita 1 scrivendo
|
||||
«Failed to authenticate: OAuth session expired and could not be refreshed» su STDOUT,
|
||||
con stderr VUOTO. Leggendo il solo stderr il motivo registrato nel DB e spedito su
|
||||
Telegram fu `uscita 1: `, cioe' niente: l'allerta diceva il COSA e non il PERCHE' (P4),
|
||||
e per risalire alla causa e' servito aprire a mano il transcript della sessione headless.
|
||||
Quando la diagnosi serve il guasto e' gia' rientrato (P3): il motivo si cattura qui o mai.
|
||||
|
||||
E se non c'e' NESSUN messaggio lo si dice, invece di lasciare i due punti a vuoto:
|
||||
"la CLI non ha detto perche'" e "il perche' l'abbiamo perso noi" sono guasti diversi.
|
||||
"""
|
||||
det = " | ".join(x for x in ((stderr or "").strip(), (stdout or "").strip()) if x)
|
||||
return f"uscita {returncode}: {det[:300] if det else 'nessun messaggio su stderr ne stdout'}"
|
||||
|
||||
|
||||
def interroga(prompt: str, modello: str = MODELLO_DEFAULT,
|
||||
timeout: int = TIMEOUT_S) -> tuple[str, str | None]:
|
||||
"""Ritorna (testo, errore). L'errore si RESTITUISCE, non si ingoia: senza, il giorno che
|
||||
@@ -163,7 +180,7 @@ def interroga(prompt: str, modello: str = MODELLO_DEFAULT,
|
||||
except subprocess.TimeoutExpired:
|
||||
return "", f"timeout dopo {timeout}s"
|
||||
if p.returncode != 0:
|
||||
return "", f"uscita {p.returncode}: {(p.stderr or '').strip()[:200]}"
|
||||
return "", motivo_uscita(p.returncode, p.stdout, p.stderr)
|
||||
return pulisci(p.stdout), None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user