{"record":{"id":"7adfc4525c58f654","repo":"wavetermdev/waveterm","slug":"failed-to-connect-client-w","errorCode":null,"errorMessage":"failed to connect client: %w","messagePattern":"failed to connect client: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/jobmanager/jobmanager.go","lineNumber":195,"sourceCode":"\t\tjm.StreamManager.ClientDisconnected()\n\t\tif oldStreamId != \"\" {\n\t\t\tmainServerConn.WshRpc.StreamBroker.DetachStreamWriter(oldStreamId)\n\t\t\tlog.Printf(\"connectToStreamHelper: detached old stream id=%s\\n\", oldStreamId)\n\t\t}\n\t\tjm.connectedStreamClient = nil\n\t}\n\tdataSender := &routedDataSender{\n\t\twshRpc: mainServerConn.WshRpc,\n\t\troute:  streamMeta.ReaderRouteId,\n\t}\n\tserverSeq, err := jm.StreamManager.ClientConnected(\n\t\tstreamMeta.Id,\n\t\tdataSender,\n\t\trwndSize,\n\t\tseq,\n\t)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to connect client: %w\", err)\n\t}\n\tjm.connectedStreamClient = mainServerConn\n\treturn serverSeq, nil\n}\n\nfunc (jm *JobManager) disconnectFromStreamHelper(mainServerConn *MainServerConn) {\n\tjm.lock.Lock()\n\tdefer jm.lock.Unlock()\n\tif jm.connectedStreamClient == nil || jm.connectedStreamClient != mainServerConn {\n\t\treturn\n\t}\n\tjm.StreamManager.ClientDisconnected()\n\tjm.connectedStreamClient = nil\n}\n\nfunc (jm *JobManager) SetAttachedClient(msc *MainServerConn) {\n\tjm.lock.Lock()\n\tdefer jm.lock.Unlock()","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobmanager/jobmanager.go#L177-L213","documentation":"connectToStreamHelper_withlock delegates the actual client attach to the StreamManager's connect call (with stream id, data sender, rwnd, and sequence number). If that returns an error, it is wrapped as 'failed to connect client', meaning the job manager could not attach the main-server client to the job's output stream.","triggerScenarios":"StartJob or PrepareConnect called with a StreamMeta whose Id collides with an existing stream, references a stale/disconnected stream, or when the underlying stream connect fails due to channel/route setup errors on the main server connection.","commonSituations":"Two clients racing to attach to the same job stream; reconnecting after a server restart with an outdated stream id; transient RPC/channel failures between the job manager daemon and the main server.","solutions":["Read the wrapped inner error to identify the stream-layer cause (duplicate stream id, unknown route, closed channel)","Retry PrepareConnect with a fresh StreamMeta.Id after the connection resets","Ensure only one client connects per job stream at a time (disconnect the old client first)","Verify the main server connection is alive and the job manager daemon is running the same version"],"exampleFix":"// before\n_, err := jm.connectToStreamHelper_withlock(msc, staleMeta, seq)\n// after\nmeta := wshrpc.StreamMeta{Id: genNewStreamId(), RWnd: DefaultRWnd}\nserverSeq, err := jm.connectToStreamHelper_withlock(msc, meta, seq)\nif err != nil {\n    msc = reconnectMainServer() // fresh conn before retry\n}","handlingStrategy":"retry","validationCode":"if streamMeta == nil || streamMeta.Id == \"\" {\n    return fmt.Errorf(\"StreamMeta with valid Id required before connecting\")\n}","typeGuard":null,"tryCatchPattern":"serverSeq, err := jm.PrepareConnect(meta)\nif err != nil && strings.Contains(err.Error(), \"failed to connect client\") {\n    time.Sleep(250 * time.Millisecond)\n    meta.Id = genNewStreamId() // fresh stream id on retry\n    serverSeq, err = jm.PrepareConnect(meta)\n}","preventionTips":["Generate a unique stream id per connection attempt","Disconnect existing clients before attaching a new one","Reconnect the main-server conn after resets before retrying","Keep exponential backoff and cap retry attempts"],"tags":["streaming","connection","jobmanager"],"backgroundTag":"stream-connect-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}