@@ -24,6 +24,8 @@ var permanentErrorStatuses = []int{
24
24
http .StatusBadRequest ,// returned if API mismatch
25
25
http .StatusNotFound ,// returned if user doesn't have permission or agent doesn't exist
26
26
http .StatusInternalServerError ,// returned if database is not reachable,
27
+ http .StatusUnauthorized ,// returned if user is not authenticated
28
+ http .StatusForbidden ,// returned if user is not authorized
27
29
}
28
30
29
31
type WebsocketDialer struct {
@@ -39,6 +41,24 @@ type WebsocketDialer struct {
39
41
isFirst bool
40
42
}
41
43
44
+ // checkResumeTokenFailure checks if the parsed error indicates a resume token failure
45
+ // and updates the resumeTokenFailed flag accordingly. Returns true if a resume token
46
+ // failure was detected.
47
+ func (w * WebsocketDialer )checkResumeTokenFailure (ctx context.Context ,sdkErr * codersdk.Error )bool {
48
+ if sdkErr == nil {
49
+ return false
50
+ }
51
+
52
+ for _ ,v := range sdkErr .Validations {
53
+ if v .Field == "resume_token" {
54
+ w .logger .Warn (ctx ,"failed to dial tailnet v2+ API: server replied invalid resume token; unsetting for next connection attempt" )
55
+ w .resumeTokenFailed = true
56
+ return true
57
+ }
58
+ }
59
+ return false
60
+ }
61
+
42
62
type WebsocketDialerOption func (* WebsocketDialer )
43
63
44
64
func WithWorkspaceUpdates (req * proto.WorkspaceUpdatesRequest )WebsocketDialerOption {
@@ -82,9 +102,14 @@ func (w *WebsocketDialer) Dial(ctx context.Context, r tailnet.ResumeTokenControl
82
102
if w .isFirst {
83
103
if res != nil && slices .Contains (permanentErrorStatuses ,res .StatusCode ) {
84
104
err = codersdk .ReadBodyAsError (res )
85
- // A bit more human-readable help in the case the API version was rejected
86
105
var sdkErr * codersdk.Error
87
106
if xerrors .As (err ,& sdkErr ) {
107
+ // Check for resume token failure first
108
+ if w .checkResumeTokenFailure (ctx ,sdkErr ) {
109
+ return tailnet.ControlProtocolClients {},err
110
+ }
111
+
112
+ // A bit more human-readable help in the case the API version was rejected
88
113
if sdkErr .Message == AgentAPIMismatchMessage &&
89
114
sdkErr .StatusCode ()== http .StatusBadRequest {
90
115
sdkErr .Helper = fmt .Sprintf (
@@ -107,13 +132,8 @@ func (w *WebsocketDialer) Dial(ctx context.Context, r tailnet.ResumeTokenControl
107
132
bodyErr := codersdk .ReadBodyAsError (res )
108
133
var sdkErr * codersdk.Error
109
134
if xerrors .As (bodyErr ,& sdkErr ) {
110
- for _ ,v := range sdkErr .Validations {
111
- if v .Field == "resume_token" {
112
- // Unset the resume token for the next attempt
113
- w .logger .Warn (ctx ,"failed to dial tailnet v2+ API: server replied invalid resume token; unsetting for next connection attempt" )
114
- w .resumeTokenFailed = true
115
- return tailnet.ControlProtocolClients {},err
116
- }
135
+ if w .checkResumeTokenFailure (ctx ,sdkErr ) {
136
+ return tailnet.ControlProtocolClients {},err
117
137
}
118
138
}
119
139
if ! errors .Is (err ,context .Canceled ) {