{"record":{"id":"23265344fc70cadd","repo":"fatedier/frp","slug":"ssh-tunnel-server-closed","errorCode":null,"errorMessage":"ssh tunnel server closed","messagePattern":"ssh tunnel server closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"pkg/ssh/server.go","lineNumber":403,"sourceCode":"\tstatusExporter := s.vc.Service().StatusExporter()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tps, ok := statusExporter.GetProxyStatus(name)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch ps.Phase {\n\t\t\tcase proxy.ProxyPhaseRunning:\n\t\t\t\treturn ps, nil\n\t\t\tcase proxy.ProxyPhaseStartErr, proxy.ProxyPhaseClosed:\n\t\t\t\treturn ps, errors.New(ps.Err)\n\t\t\t}\n\t\tcase <-timer.C:\n\t\t\treturn nil, fmt.Errorf(\"wait proxy status ready timeout\")\n\t\tcase <-s.doneCh:\n\t\t\treturn nil, fmt.Errorf(\"ssh tunnel server closed\")\n\t\t}\n\t}\n}\n","sourceCodeStart":385,"sourceCodeEnd":407,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/ssh/server.go#L385-L407","documentation":"Returned by waitProxyStatusReady when the TunnelServer's doneCh is closed while it is still waiting for a proxy to become ready. doneCh is closed by the server's shutdown path, so this error means 'we did not learn the proxy's fate because the whole SSH tunnel server is going away' — a clean cancellation, not a proxy failure.","triggerScenarios":"frps shuts down or the SSH gateway listener is closed while an SSH tunnel session is still waiting for its proxy to register; context cancellation propagating through server Close().","commonSituations":"frps restart/redeploy while users have tunnels mid-creation; systemd stop; supervisor-driven rolling restarts.","solutions":["Treat as transient: reconnect the SSH tunnel after the server comes back up.","Operators: drain or wait for tunnel setup quiescence before restarting frps.","Automation: distinguish this cancellation (retry) from 'wait proxy status ready timeout' (investigate proxy)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"status, err := s.waitProxyStatusReady(name, timeout)\nif err != nil {\n    if strings.Contains(err.Error(), \"ssh tunnel server closed\") {\n        return nil // clean cancellation during shutdown — not a failure\n    }\n    return err\n}","preventionTips":["Distinguish this cancellation from real proxy failures before alerting.","Operators: drain tunnel setup before restarting frps.","Clients: auto-reconnect tunnels after the server returns."],"tags":["ssh","shutdown","cancellation","lifecycle"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}