wavetermdev/waveterm · error

no pending stream (call PrepareConnect first)

Error message

no pending stream (call PrepareConnect first)

What it means

StartStream in pkg/jobmanager/jobmanager.go returns this when jm.pendingStreamMeta is nil, meaning the streaming handshake never staged its metadata. The JobManager requires the two-phase flow: PrepareConnect populates pendingStreamMeta, then StartStream consumes it and attaches the stream writer. Calling StartStream out of order leaves nothing to attach, so the library refuses with this sentinel-style message.

Source

Thrown at pkg/jobmanager/jobmanager.go:361

		rtnData.HasExited = true
		rtnData.ExitCode = exitData.ExitCode
		rtnData.ExitSignal = exitData.ExitSignal
		rtnData.ExitErr = exitData.ExitErr
	}

	log.Printf("PrepareConnect: streamid=%s clientSeq=%d serverSeq=%d streamDone=%v streamError=%q hasExited=%v\n", data.StreamMeta.Id, data.Seq, rtnData.Seq, rtnData.StreamDone, rtnData.StreamError, hasExited)
	return rtnData, nil
}

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 {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Call jm.PrepareConnect() (and check its error) before jm.StartStream()
  2. Do not call StartStream twice for the same stream; once it succeeds, pendingStreamMeta is nil and a new PrepareConnect is required
  3. Add an assertion or log when pendingStreamMeta is nil at StartStream entry to catch ordering bugs early

Example fix

// before
if err := jm.StartStream(); err != nil {
    log.Printf("start stream failed: %v", err)
}
// after
if err := jm.PrepareConnect(); err != nil {
    log.Printf("prepare connect failed: %v", err)
    return err
}
if err := jm.StartStream(); err != nil {
    log.Printf("start stream failed: %v", err)
}
Defensive patterns

Strategy: validation

Validate before calling

if err := jm.PrepareConnect(); err != nil {
    return fmt.Errorf("prepare failed: %w", err)
}
// only now safe to call StartStream

Try / catch

if err := jm.StartStream(); err != nil {
    if strings.Contains(err.Error(), "no pending stream") {
        // ordering bug: re-run PrepareConnect
        if perr := jm.PrepareConnect(); perr == nil {
            err = jm.StartStream()
        }
    }
    return err
}

Prevention

When it happens

Trigger: Calling jm.StartStream() without a prior successful jm.PrepareConnect() call, or after a previous StartStream already consumed pendingStreamMeta (it is set to nil on success), or after PrepareConnect returned an error and never set the metadata.

Common situations: Wiring the job manager's public methods in the wrong order during setup; retrying StartStream after a successful start; a race where two goroutines both call StartStream and the second finds pendingStreamMeta already cleared; refactoring that dropped the PrepareConnect step.

Related errors


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