{"record":{"id":"28127b4f46f9b0cd","repo":"wavetermdev/waveterm","slug":"no-pending-stream-call-prepareconnect-first","errorCode":null,"errorMessage":"no pending stream (call PrepareConnect first)","messagePattern":"no pending stream \\(call PrepareConnect first\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/jobmanager/jobmanager.go","lineNumber":361,"sourceCode":"\t\trtnData.HasExited = true\n\t\trtnData.ExitCode = exitData.ExitCode\n\t\trtnData.ExitSignal = exitData.ExitSignal\n\t\trtnData.ExitErr = exitData.ExitErr\n\t}\n\n\tlog.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)\n\treturn rtnData, nil\n}\n\nfunc (jm *JobManager) StartStream(msc *MainServerConn) error {\n\tjm.lock.Lock()\n\tdefer jm.lock.Unlock()\n\n\tif jm.Cmd == nil {\n\t\treturn fmt.Errorf(\"job not started\")\n\t}\n\tif jm.pendingStreamMeta == nil {\n\t\treturn fmt.Errorf(\"no pending stream (call PrepareConnect first)\")\n\t}\n\n\terr := msc.WshRpc.StreamBroker.AttachStreamWriter(jm.pendingStreamMeta, jm.StreamManager)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to attach stream writer: %w\", err)\n\t}\n\n\terr = jm.StreamManager.SetRwndSize(int(jm.pendingStreamMeta.RWnd))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set rwnd size: %w\", err)\n\t}\n\n\tlog.Printf(\"StartStream: streamid=%s rwnd=%d streaming started\\n\", jm.pendingStreamMeta.Id, jm.pendingStreamMeta.RWnd)\n\tjm.pendingStreamMeta = nil\n\treturn nil\n}\n\nfunc MakeJobDomainSocket(clientId string, jobId string) error {","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobmanager/jobmanager.go#L343-L379","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call jm.PrepareConnect() (and check its error) before jm.StartStream()","Do not call StartStream twice for the same stream; once it succeeds, pendingStreamMeta is nil and a new PrepareConnect is required","Add an assertion or log when pendingStreamMeta is nil at StartStream entry to catch ordering bugs early"],"exampleFix":"// before\nif err := jm.StartStream(); err != nil {\n    log.Printf(\"start stream failed: %v\", err)\n}\n// after\nif err := jm.PrepareConnect(); err != nil {\n    log.Printf(\"prepare connect failed: %v\", err)\n    return err\n}\nif err := jm.StartStream(); err != nil {\n    log.Printf(\"start stream failed: %v\", err)\n}","handlingStrategy":"validation","validationCode":"if err := jm.PrepareConnect(); err != nil {\n    return fmt.Errorf(\"prepare failed: %w\", err)\n}\n// only now safe to call StartStream","typeGuard":null,"tryCatchPattern":"if err := jm.StartStream(); err != nil {\n    if strings.Contains(err.Error(), \"no pending stream\") {\n        // ordering bug: re-run PrepareConnect\n        if perr := jm.PrepareConnect(); perr == nil {\n            err = jm.StartStream()\n        }\n    }\n    return err\n}","preventionTips":["Always call PrepareConnect immediately before StartStream in a single helper function","Never reuse a JobManager for a second StartStream without a fresh PrepareConnect","Guard concurrent StartStream calls with a mutex or once-guard","Log the two-phase lifecycle so ordering regressions are visible"],"tags":["go","jobmanager","streaming","api-misuse","state-error"],"backgroundTag":"call-prepareconnect-first","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}