wavetermdev/waveterm · error

failed to set rwnd size: %w

Error message

failed to set rwnd size: %w

What it means

After a successful attach, StartStream applies the receiver-window size negotiated in pendingStreamMeta via StreamManager.SetRwndSize. This error wraps any failure from that call. It indicates the stream is attached but flow-control configuration could not be applied, so the stream is left partially initialized.

Source

Thrown at pkg/jobmanager/jobmanager.go:371

func (jm *JobManager) StartStream(msc *MainServerConn) error {
	jm.lock.Lock()
	defer jm.lock.Unlock()

	if jm.Cmd == nil {
		return fmt.Errorf("job not started")
	}
	if jm.pendingStreamMeta == nil {
		return fmt.Errorf("no pending stream (call PrepareConnect first)")
	}

	err := msc.WshRpc.StreamBroker.AttachStreamWriter(jm.pendingStreamMeta, jm.StreamManager)
	if err != nil {
		return fmt.Errorf("failed to attach stream writer: %w", err)
	}

	err = jm.StreamManager.SetRwndSize(int(jm.pendingStreamMeta.RWnd))
	if err != nil {
		return fmt.Errorf("failed to set rwnd size: %w", err)
	}

	log.Printf("StartStream: streamid=%s rwnd=%d streaming started\n", jm.pendingStreamMeta.Id, jm.pendingStreamMeta.RWnd)
	jm.pendingStreamMeta = nil
	return nil
}

func MakeJobDomainSocket(clientId string, jobId string) error {
	socketDir := filepath.Join("/tmp", fmt.Sprintf("waveterm-%d", os.Getuid()))
	err := os.MkdirAll(socketDir, 0700)
	if err != nil {
		return fmt.Errorf("failed to create socket directory: %w", err)
	}

	socketPath := wavebase.GetRemoteJobSocketPath(jobId)

	os.Remove(socketPath)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Log jm.pendingStreamMeta.RWnd and the wrapped error to see whether the negotiated value is sane
  2. Validate RWnd is > 0 (and within protocol bounds) before calling StartStream; re-run PrepareConnect if not
  3. Re-do the PrepareConnect/StartStream cycle to renegotiate a fresh window size
  4. Check StreamManager initialization order so SetRwndSize runs on a ready stream

Example fix

// before
jm.StartStream() // trusts pendingStreamMeta.RWnd
// after
if jm.pendingStreamMeta == nil || jm.pendingStreamMeta.RWnd == 0 {
    return fmt.Errorf("invalid rwnd negotiated, re-run PrepareConnect")
}
if err := jm.StartStream(); err != nil {
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

if jm.pendingStreamMeta == nil || jm.pendingStreamMeta.RWnd == 0 {
    return fmt.Errorf("invalid stream metadata/rwnd, re-run PrepareConnect")
}

Type guard

func validPendingStream(jm *JobManager) bool {
    return jm.pendingStreamMeta != nil && jm.pendingStreamMeta.RWnd > 0
}

Try / catch

if err := jm.StartStream(); err != nil {
    if strings.HasPrefix(err.Error(), "failed to set rwnd size") {
        log.Printf("rwnd error: %v", errors.Unwrap(err))
        // renegotiate and retry
        if perr := jm.PrepareConnect(); perr == nil {
            err = jm.StartStream()
        }
    }
    return err
}

Prevention

When it happens

Trigger: SetRwndSize returns an error — typically an invalid, zero, or out-of-range RWnd value in pendingStreamMeta, or the StreamManager rejecting the resize for an attached/invalid stream state.

Common situations: Peer negotiated a bad RWnd value during PrepareConnect; integer overflow/widening issue in int(jm.pendingStreamMeta.RWnd); StreamManager not fully initialized before StartStream; version skew where window-size semantics changed.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/ed5978ec3d043dc9. Report an issue: GitHub.