{"record":{"id":"166e7b55a1e525ca","repo":"wavetermdev/waveterm","slug":"cannot-update-wsh-ssh-client-is-not-connected-166e7b","errorCode":null,"errorMessage":"cannot update wsh: ssh client is not connected","messagePattern":"cannot update wsh: ssh client is not connected","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wslconn/wslconn.go","lineNumber":369,"sourceCode":"type WshInstallOpts struct {\n\tForce        bool\n\tNoUserPrompt bool\n}\n\nvar queryTextTemplate = strings.TrimSpace(`\nWave requires Wave Shell Extensions to be\ninstalled on %q\nto ensure a seamless experience.\n\nWould you like to install them?\n`)\n\nfunc (conn *WslConn) UpdateWsh(ctx context.Context, clientDisplayName string, remoteInfo *wshrpc.RemoteInfo) error {\n\tconn.Infof(ctx, \"attempting to update wsh for connection %s (os:%s arch:%s version:%s)\\n\",\n\t\tconn.GetName(), remoteInfo.ClientOs, remoteInfo.ClientArch, remoteInfo.ClientVersion)\n\tclient := conn.GetClient()\n\tif client == nil {\n\t\treturn fmt.Errorf(\"cannot update wsh: ssh client is not connected\")\n\t}\n\terr := CpWshToRemote(ctx, client, remoteInfo.ClientOs, remoteInfo.ClientArch)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error installing wsh to remote: %w\", err)\n\t}\n\tconn.Infof(ctx, \"successfully updated wsh on %s\\n\", conn.GetName())\n\treturn nil\n\n}\n\n// returns (allowed, error)\nfunc (conn *WslConn) getPermissionToInstallWsh(ctx context.Context, clientDisplayName string) (bool, error) {\n\tconn.Infof(ctx, \"running getPermissionToInstallWsh...\\n\")\n\tqueryText := fmt.Sprintf(queryTextTemplate, clientDisplayName)\n\ttitle := \"Install Wave Shell Extensions\"\n\trequest := &userinput.UserInputRequest{\n\t\tResponseType: \"confirm\",\n\t\tQueryText:    queryText,","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wslconn/wslconn.go#L351-L387","documentation":"UpdateWsh needs the underlying *wsl.Distro client to copy the wsh binary into the distro. If conn.GetClient() returns nil, the SSH/WSL client handle has not been established (or was cleared on disconnect), so there is no channel through which to install the update and the call fails immediately before any copy is attempted.","triggerScenarios":"Calling WslConn.UpdateWsh while the WSL distro is disconnected, before Connect() has completed, or after the connection was closed/reset so conn.Client is nil.","commonSituations":"Triggering a wsh update from the UI while the connection is in Status_Disconnected or Status_Init; a connection that dropped and its client was cleared, then an update job fires; calling UpdateWsh programmatically without first calling Connect/WaitForConnect.","solutions":["Reconnect the distro first: call conn.Connect(ctx) or conn.Reconnect(ctx), then retry UpdateWsh","Wait for readiness with conn.WaitForConnect(ctx) before issuing the update","Check the connection status (conn.DeriveConnStatus) and only update when Status_Connected"],"exampleFix":"// before\nerr := conn.UpdateWsh(ctx, name, remoteInfo)\n// after\nif err := conn.WaitForConnect(ctx); err != nil {\n    return fmt.Errorf(\"connection not ready: %w\", err)\n}\nerr := conn.UpdateWsh(ctx, name, remoteInfo)","handlingStrategy":"validation","validationCode":"if conn.DeriveConnStatus().Status != Status_Connected {\n    return fmt.Errorf(\"cannot update wsh: connection %s is not connected\", conn.GetName())\n}\n// safe to call UpdateWsh now","typeGuard":"func wslClientReady(conn *wslconn.WslConn) bool {\n    return conn.DeriveConnStatus().Status == wslconn.Status_Connected\n}","tryCatchPattern":"if err := conn.UpdateWsh(ctx, name, remoteInfo); err != nil {\n    if strings.Contains(err.Error(), \"ssh client is not connected\") {\n        if cerr := conn.Connect(ctx); cerr == nil {\n            err = conn.UpdateWsh(ctx, name, remoteInfo)\n        }\n    }\n}","preventionTips":["Always check DeriveConnStatus before mutating wsh on a connection","Serialize update calls with connection lifecycle (no updates during reconnect)","WaitForConnect before programmatic update jobs"],"tags":["wsl","wsh","not-connected","update"],"backgroundTag":"ssh-client-not-connected","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}