From cbd14e9cd002c2757eb5a426b3c8c14edb3cc4e3 Mon Sep 17 00:00:00 2001 From: damocles Date: Tue, 2 Jun 2026 11:23:40 +0200 Subject: [PATCH] fix(#1066): retry turn once on 401 before parking for re-login --- docs/turn-loop.md | 6 +++++- hive-ag3nt/src/turn.rs | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/turn-loop.md b/docs/turn-loop.md index 112f79fe..3ec1bd0a 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -34,7 +34,11 @@ binary, not two) runs: while the harness is parked. **Auth-failed detection**: both stdout and stderr pumps also match `AUTH_FAIL_MARKERS` (`"authentication_failed"`, `401`, etc.). - On match the harness writes `{state_dir}/hyperhive-needs-login`, + On the first 401, `drive_turn` retries the same prompt once + immediately (transient token-refresh races and brief API hiccups + can cause a 401 that clears on retry). Only if the retry also + returns `AuthFailed` does `drive_turn` bubble it up to the serve + loop, which then writes `{state_dir}/hyperhive-needs-login`, emits `needs_login_idle` status, requeues the inflight message (so it replays after re-auth), and parks in `wait_for_login` — the same path used at boot. The operator re-authenticates via diff --git a/hive-ag3nt/src/turn.rs b/hive-ag3nt/src/turn.rs index 88057c69..6cd6e7e3 100644 --- a/hive-ag3nt/src/turn.rs +++ b/hive-ag3nt/src/turn.rs @@ -320,6 +320,16 @@ pub async fn drive_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutco // Rate-limited: no point retrying immediately — bubble up so the // serve loop can park + emit status before the next attempt. TurnOutcome::RateLimited => return TurnOutcome::RateLimited, + // Auth failed: may be a transient token-refresh race or brief API + // hiccup. Retry once before bubbling up so the serve loop parks for + // re-login. The retry outcome is passed through unchanged — if it + // fails again the serve loop handles it as usual. + TurnOutcome::AuthFailed => { + bus.emit(LiveEvent::Note { + text: "got 401 — retrying once before parking for re-login".into(), + }); + run_turn(prompt, files, bus).await + } other => other, }; // Proactive: a turn just completed on a still-healthy session. If its