Skip to content

Commit 052f193

Browse files
committed
fix(auth): classify transport errors as home_unavailable with retryable flag
- Updated error handling in `RPopAuth` to distinguish `auth_not_found` from transport errors. - Added a new test, `TestPickNextViaHomeClassifiesTransportErrorsAsHomeUnavailable`, to validate correct error classification and retryable property.
1 parent ae6c5ea commit 052f193

2 files changed

Lines changed: 48 additions & 1 deletion

File tree

sdk/cliproxy/auth/conductor.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4178,7 +4178,10 @@ func (m *Manager) pickNextViaHome(ctx context.Context, model string, opts clipro
41784178

41794179
raw, err := client.RPopAuth(ctx, requestedModel, sessionID, dispatchHeaders, count)
41804180
if err != nil {
4181-
return nil, nil, "", &Error{Code: "auth_not_found", Message: err.Error(), HTTPStatus: http.StatusServiceUnavailable}
4181+
if errors.Is(err, home.ErrAuthNotFound) {
4182+
return nil, nil, "", &Error{Code: "auth_not_found", Message: err.Error(), HTTPStatus: http.StatusServiceUnavailable}
4183+
}
4184+
return nil, nil, "", &Error{Code: "home_unavailable", Message: err.Error(), Retryable: true, HTTPStatus: http.StatusServiceUnavailable}
41824185
}
41834186

41844187
var env homeErrorEnvelope

sdk/cliproxy/auth/home_websocket_reuse_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,50 @@ func TestPickNextViaHomeDoesNotReusePinnedNonWebsocketAuth(t *testing.T) {
221221
}
222222
}
223223

224+
type homeAuthTransportErrorDispatcher struct {
225+
err error
226+
}
227+
228+
func (d homeAuthTransportErrorDispatcher) HeartbeatOK() bool {
229+
return true
230+
}
231+
232+
func (d homeAuthTransportErrorDispatcher) RPopAuth(context.Context, string, string, http.Header, int) ([]byte, error) {
233+
return nil, d.err
234+
}
235+
236+
func TestPickNextViaHomeClassifiesTransportErrorsAsHomeUnavailable(t *testing.T) {
237+
dispatcher := homeAuthTransportErrorDispatcher{err: errors.New("read tcp 127.0.0.1:46704->127.0.0.1:8327: i/o timeout")}
238+
oldCurrentHomeDispatcher := currentHomeDispatcher
239+
currentHomeDispatcher = func() homeAuthDispatcher {
240+
return dispatcher
241+
}
242+
t.Cleanup(func() {
243+
currentHomeDispatcher = oldCurrentHomeDispatcher
244+
})
245+
246+
manager := NewManager(nil, nil, nil)
247+
manager.SetConfig(&internalconfig.Config{Home: internalconfig.HomeConfig{Enabled: true}})
248+
249+
_, _, _, errPick := manager.pickNextViaHome(context.Background(), "gpt-5.4", cliproxyexecutor.Options{}, nil)
250+
if errPick == nil {
251+
t.Fatal("pickNextViaHome() error is nil, want home unavailable error")
252+
}
253+
var authErr *Error
254+
if !errors.As(errPick, &authErr) {
255+
t.Fatalf("pickNextViaHome() error = %T, want *Error", errPick)
256+
}
257+
if authErr.Code != "home_unavailable" {
258+
t.Fatalf("pickNextViaHome() error code = %q, want home_unavailable (%v)", authErr.Code, errPick)
259+
}
260+
if authErr.StatusCode() != http.StatusServiceUnavailable {
261+
t.Fatalf("pickNextViaHome() status = %d, want %d", authErr.StatusCode(), http.StatusServiceUnavailable)
262+
}
263+
if !authErr.Retryable {
264+
t.Fatal("pickNextViaHome() retryable = false, want true")
265+
}
266+
}
267+
224268
func TestHomeRuntimeAuthsClearWhenHomeDisabled(t *testing.T) {
225269
manager := NewManager(nil, nil, nil)
226270
manager.SetConfig(&internalconfig.Config{Home: internalconfig.HomeConfig{Enabled: true}})

0 commit comments

Comments
 (0)