wavetermdev/waveterm · error
failed to connect client: %w
Error message
failed to connect client: %w
What it means
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.
Source
Thrown at pkg/jobmanager/jobmanager.go:195
jm.StreamManager.ClientDisconnected()
if oldStreamId != "" {
mainServerConn.WshRpc.StreamBroker.DetachStreamWriter(oldStreamId)
log.Printf("connectToStreamHelper: detached old stream id=%s\n", oldStreamId)
}
jm.connectedStreamClient = nil
}
dataSender := &routedDataSender{
wshRpc: mainServerConn.WshRpc,
route: streamMeta.ReaderRouteId,
}
serverSeq, err := jm.StreamManager.ClientConnected(
streamMeta.Id,
dataSender,
rwndSize,
seq,
)
if err != nil {
return 0, fmt.Errorf("failed to connect client: %w", err)
}
jm.connectedStreamClient = mainServerConn
return serverSeq, nil
}
func (jm *JobManager) disconnectFromStreamHelper(mainServerConn *MainServerConn) {
jm.lock.Lock()
defer jm.lock.Unlock()
if jm.connectedStreamClient == nil || jm.connectedStreamClient != mainServerConn {
return
}
jm.StreamManager.ClientDisconnected()
jm.connectedStreamClient = nil
}
func (jm *JobManager) SetAttachedClient(msc *MainServerConn) {
jm.lock.Lock()
defer jm.lock.Unlock()View on GitHub (pinned to a4447c1563)
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
Example fix
// before
_, err := jm.connectToStreamHelper_withlock(msc, staleMeta, seq)
// after
meta := wshrpc.StreamMeta{Id: genNewStreamId(), RWnd: DefaultRWnd}
serverSeq, err := jm.connectToStreamHelper_withlock(msc, meta, seq)
if err != nil {
msc = reconnectMainServer() // fresh conn before retry
} Defensive patterns
Strategy: retry
Validate before calling
if streamMeta == nil || streamMeta.Id == "" {
return fmt.Errorf("StreamMeta with valid Id required before connecting")
} Try / catch
serverSeq, err := jm.PrepareConnect(meta)
if err != nil && strings.Contains(err.Error(), "failed to connect client") {
time.Sleep(250 * time.Millisecond)
meta.Id = genNewStreamId() // fresh stream id on retry
serverSeq, err = jm.PrepareConnect(meta)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- no pending stream (call PrepareConnect first)
- msg.error
- sse handler is nil
- sse handler is nil
- sse handler is nil
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/7adfc4525c58f654.
Report an issue: GitHub.