detached d63c59a

fix(remote): close stale attachment handshakes safely

commit
d63c59a4c7bd874c8f07034c28d36016583b3fde
author
brice · · 2026-08-08 17:16 UTC
parent
92a7932
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +7 -7
M internal/usecase/daemon/daemon.go +6 -6
@@ -1167,10 +1167,7 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut <
11671167 if admitted {
11681168 welcomeTicket.End()
11691169 }
1170- if !d.abortResumeClaim(ac) {
1171- d.clientGone(sess, ac, tr, false)
1172- }
1173- _ = tr.Close()
1170+ failAttachment()
11741171 return
11751172 }
11761173 if err := handshakeCtx.Err(); err != nil {
@@ -1199,27 +1196,30 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut <
11991196 failAttachment()
12001197 return
12011198 }
1202- if postWelcomeLease != nil && (rc == nil || !rc.markAttachmentReady(postWelcomeLease)) {
1199+ postWelcomeRC := sess.renderCoordinator()
1200+ if postWelcomeLease != nil && (postWelcomeRC == nil || !postWelcomeRC.markAttachmentReady(postWelcomeLease)) {
12031201 postWelcomeTicket.End()
12041202 // The attachment was detached while Welcome was in flight; never let
12051203 // this stale handshake emit an Output frame.
12061204 failAttachment()
12071205 return
12081206 }
1209- postWelcomeTicket.End()
12101207 paintToken := sess.attachmentToken(ac, tr)
12111208 painted := make(chan bool, 1)
12121209 if err := boundedHandshakeOperation(handshakeCtx, tr, func() error {
12131210 painted <- d.firstPaintForTransition(paintToken)
12141211 return nil
12151212 }); err != nil {
1213+ postWelcomeTicket.End()
12161214 failAttachment()
12171215 return
12181216 }
12191217 if !<-painted || handshakeCtx.Err() != nil {
1218+ postWelcomeTicket.End()
12201219 failAttachment()
12211220 return
12221221 }
1222+ postWelcomeTicket.End()
12231223 if !d.commitResumeClaim(ac) {
12241224 failAttachment()
12251225 return
@@ -1167,10 +1167,7 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut <
1167 if admitted {1167 if admitted {
1168 welcomeTicket.End()1168 welcomeTicket.End()
1169 }1169 }
1170- if !d.abortResumeClaim(ac) {1170+ failAttachment()
1171- d.clientGone(sess, ac, tr, false)
1172- }
1173- _ = tr.Close()
1174 return1171 return
1175 }1172 }
1176 if err := handshakeCtx.Err(); err != nil {1173 if err := handshakeCtx.Err(); err != nil {
@@ -1199,27 +1196,30 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut <
1199 failAttachment()1196 failAttachment()
1200 return1197 return
1201 }1198 }
1202- if postWelcomeLease != nil && (rc == nil || !rc.markAttachmentReady(postWelcomeLease)) {1199+ postWelcomeRC := sess.renderCoordinator()
1200+ if postWelcomeLease != nil && (postWelcomeRC == nil || !postWelcomeRC.markAttachmentReady(postWelcomeLease)) {
1203 postWelcomeTicket.End()1201 postWelcomeTicket.End()
1204 // The attachment was detached while Welcome was in flight; never let1202 // The attachment was detached while Welcome was in flight; never let
1205 // this stale handshake emit an Output frame.1203 // this stale handshake emit an Output frame.
1206 failAttachment()1204 failAttachment()
1207 return1205 return
1208 }1206 }
1209- postWelcomeTicket.End()
1210 paintToken := sess.attachmentToken(ac, tr)1207 paintToken := sess.attachmentToken(ac, tr)
1211 painted := make(chan bool, 1)1208 painted := make(chan bool, 1)
1212 if err := boundedHandshakeOperation(handshakeCtx, tr, func() error {1209 if err := boundedHandshakeOperation(handshakeCtx, tr, func() error {
1213 painted <- d.firstPaintForTransition(paintToken)1210 painted <- d.firstPaintForTransition(paintToken)
1214 return nil1211 return nil
1215 }); err != nil {1212 }); err != nil {
1213+ postWelcomeTicket.End()
1216 failAttachment()1214 failAttachment()
1217 return1215 return
1218 }1216 }
1219 if !<-painted || handshakeCtx.Err() != nil {1217 if !<-painted || handshakeCtx.Err() != nil {
1218+ postWelcomeTicket.End()
1220 failAttachment()1219 failAttachment()
1221 return1220 return
1222 }1221 }
1222+ postWelcomeTicket.End()
1223 if !d.commitResumeClaim(ac) {1223 if !d.commitResumeClaim(ac) {
1224 failAttachment()1224 failAttachment()
1225 return1225 return
M internal/usecase/daemon/picker.go +1 -1
@@ -940,7 +940,7 @@ func (d *Daemon) remotePickerTargetReady(key domain.RemoteSessionKey) bool {
940940 }
941941 for _, session := range entry.Sessions {
942942 if session.Name == key.Name {
943- return true
943+ return !remoteSessionStateStopped(session.State) && session.State != "broken"
944944 }
945945 }
946946 return false
@@ -940,7 +940,7 @@ func (d *Daemon) remotePickerTargetReady(key domain.RemoteSessionKey) bool {
940 }940 }
941 for _, session := range entry.Sessions {941 for _, session := range entry.Sessions {
942 if session.Name == key.Name {942 if session.Name == key.Name {
943- return true943+ return !remoteSessionStateStopped(session.State) && session.State != "broken"
944 }944 }
945 }945 }
946 return false946 return false

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close