{"record":{"id":"f8a43ee9e7a5f0ed","repo":"wavetermdev/waveterm","slug":"job-is-not-connected-status-s","errorCode":null,"errorMessage":"job is not connected (status: %s)","messagePattern":"job is not connected \\(status: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/jobcontroller/jobcontroller.go","lineNumber":597,"sourceCode":"}\n\nfunc CheckJobConnected(ctx context.Context, jobId string) (*waveobj.Job, error) {\n\tjob, err := wstore.DBMustGet[*waveobj.Job](ctx, jobId)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get job: %w\", err)\n\t}\n\n\tisConnected, err := conncontroller.IsConnected(job.Connection)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error checking connection status: %w\", err)\n\t}\n\tif !isConnected {\n\t\treturn nil, fmt.Errorf(\"connection %q is not connected\", job.Connection)\n\t}\n\n\tjobConnStatus := GetJobConnStatus(jobId)\n\tif jobConnStatus != JobConnStatus_Connected {\n\t\treturn nil, fmt.Errorf(\"job is not connected (status: %s)\", jobConnStatus)\n\t}\n\n\treturn job, nil\n}\n\ntype StartJobParams struct {\n\tConnName string\n\tJobKind  string\n\tCmd      string\n\tArgs     []string\n\tEnv      map[string]string\n\tTermSize *waveobj.TermSize\n\tBlockId  string\n}\n\nfunc StartJob(ctx context.Context, params StartJobParams) (string, error) {\n\tif params.ConnName == \"\" {\n\t\treturn \"\", fmt.Errorf(\"connection name is required\")","sourceCodeStart":579,"sourceCodeEnd":615,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobcontroller/jobcontroller.go#L579-L615","documentation":"CheckJobConnected performs a two-step check: the raw connection must be up, and GetJobConnStatus(jobId) must return JobConnStatus_Connected. This error means the in-memory job-level connection status disagrees with the transport state — the conn is up but the job itself hasn't (or no longer) registered as connected.","triggerScenarios":"Calling CheckJobConnected when GetJobConnStatus returns any status other than Connected — job starting up, job torn down on the remote side, or stale job-status map entry after a reconnect.","commonSituations":"Sending input immediately after StartJob before the job's connection handshake finishes; remote shell exited and the status degraded; reconnect race where conn is up but the job is not yet reattached.","solutions":["Wait briefly and retry until job status becomes Connected","Restart the job if its status is terminally degraded","Check logs for the remote job's exit reason","Serialize SendInput behind a connected-state gate instead of calling directly"],"exampleFix":"// before\n_, err := jobcontroller.CheckJobConnected(ctx, jobId)\nif err != nil {\n\treturn err\n}\n// after\nerr := waitForCondition(func() bool {\n\t_, err := jobcontroller.CheckJobConnected(ctx, jobId)\n\treturn err == nil\n}, 5*time.Second)\nif err != nil {\n\treturn fmt.Errorf(\"job %s never became connected: %w\", jobId, err)\n}","handlingStrategy":"retry","validationCode":"if jobcontroller.GetJobConnStatus(jobId) != jobcontroller.JobConnStatus_Connected {\n\treturn fmt.Errorf(\"job %s not ready for input\", jobId)\n}","typeGuard":"func isJobStatusMismatchErr(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"job is not connected (status:\")\n}","tryCatchPattern":"_, err := jobcontroller.CheckJobConnected(ctx, jobId)\nif isJobStatusMismatchErr(err) {\n\ttime.Sleep(250 * time.Millisecond)\n\t_, err = jobcontroller.CheckJobConnected(ctx, jobId)\n}\nif err != nil { return err }","preventionTips":["After StartJob, wait for Connected status before sending input","Poll job status with a bounded retry window","Restart the job if it stays non-Connected","Watch for status transitions on reconnect races"],"tags":["job-controller","connection-state","race-condition"],"backgroundTag":"job-not-connected","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}