hive-claude: recognize OAuth-refresh give-up as an auth failure
This commit is contained in:
parent
7908332168
commit
d580270263
1 changed files with 21 additions and 4 deletions
|
|
@ -32,13 +32,17 @@ const RATE_LIMIT_MARKERS: [&str; 5] = [
|
||||||
"Request rate limit exceeded",
|
"Request rate limit exceeded",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Substrings indicating the API rejected the request as unauthenticated (401)
|
/// Substrings indicating the session is unauthenticated — either the API
|
||||||
/// — an expired/revoked OAuth session. Sourced from claude-code's `api_retry`
|
/// rejected the request (401, an expired/revoked OAuth session) or the CLI
|
||||||
/// JSON events and its human-readable give-up line.
|
/// gave up refreshing its local OAuth token before ever making a request.
|
||||||
const AUTH_FAIL_MARKERS: [&str; 3] = [
|
/// 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\":\"authentication_failed\"",
|
||||||
"\"error_status\":401",
|
"\"error_status\":401",
|
||||||
"Failed to authenticate. API Error: 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
|
/// Substrings indicating `--resume` could not resolve its target: no session
|
||||||
|
|
@ -314,6 +318,19 @@ mod tests {
|
||||||
assert!(matches!(s.soft_error(), Some(Error::PromptTooLong)));
|
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]
|
#[test]
|
||||||
fn rate_limit_still_only_on_error_event() {
|
fn rate_limit_still_only_on_error_event() {
|
||||||
// A non-error control event mentioning the marker must not trip it.
|
// A non-error control event mentioning the marker must not trip it.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue