hive-claude: recognize OAuth-refresh give-up as an auth failure

This commit is contained in:
damocles 2026-07-27 21:21:36 +02:00
commit d580270263

View file

@ -32,13 +32,17 @@ const RATE_LIMIT_MARKERS: [&str; 5] = [
"Request rate limit exceeded",
];
/// Substrings indicating the API rejected the request as unauthenticated (401)
/// — an expired/revoked OAuth session. Sourced from claude-code's `api_retry`
/// JSON events and its human-readable give-up line.
const AUTH_FAIL_MARKERS: [&str; 3] = [
/// Substrings indicating the session is unauthenticated — either the API
/// rejected the request (401, an expired/revoked OAuth session) or the CLI
/// gave up refreshing its local OAuth token before ever making a request.
/// Sourced from claude-code's `api_retry` JSON events and its human-readable
/// give-up lines (two distinct give-up messages: a rejected request, and a
/// failed local token refresh).
const AUTH_FAIL_MARKERS: [&str; 4] = [
"\"error\":\"authentication_failed\"",
"\"error_status\":401",
"Failed to authenticate. API Error: 401",
"Failed to authenticate: OAuth session expired and could not be refreshed",
];
/// Substrings indicating `--resume` could not resolve its target: no session
@ -314,6 +318,19 @@ mod tests {
assert!(matches!(s.soft_error(), Some(Error::PromptTooLong)));
}
#[test]
fn oauth_refresh_failure_trips_auth_failed() {
// A local OAuth token-refresh failure never reaches an API request —
// no `error_status`/401 anywhere — so it needs its own marker rather
// than riding the request-rejected 401 text. Observed in the wild as
// a turn that failed with a bare non-zero exit and no recognized
// sentinel: the harness never transitioned to needs-login because
// this exact give-up line wasn't a marker yet.
let s = Sentinels::default();
s.scan_cli_line("Failed to authenticate: OAuth session expired and could not be refreshed");
assert!(matches!(s.soft_error(), Some(Error::AuthFailed)));
}
#[test]
fn rate_limit_still_only_on_error_event() {
// A non-error control event mentioning the marker must not trip it.