{"record":{"id":"03f3af09de2b6454","repo":"wavetermdev/waveterm","slug":"cannot-start-remote-job-no-router-available","errorCode":null,"errorMessage":"cannot start remote job: no router available","messagePattern":"cannot start remote job: no router available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshrpc/wshremote/wshremote_job.go","lineNumber":136,"sourceCode":"func (impl *ServerImpl) removeJobManagerConnection(jobId string) {\n\timpl.Lock.Lock()\n\tdefer impl.Lock.Unlock()\n\tif _, exists := impl.JobManagerMap[jobId]; exists {\n\t\tdelete(impl.JobManagerMap, jobId)\n\t\tlog.Printf(\"removeJobManagerConnection: removed job manager connection for jobid=%s\\n\", jobId)\n\t}\n}\n\nfunc (impl *ServerImpl) getJobManagerConnection(jobId string) *JobManagerConnection {\n\timpl.Lock.Lock()\n\tdefer impl.Lock.Unlock()\n\treturn impl.JobManagerMap[jobId]\n}\n\nfunc (impl *ServerImpl) RemoteStartJobCommand(ctx context.Context, data wshrpc.CommandRemoteStartJobData) (*wshrpc.CommandStartJobRtnData, error) {\n\tlog.Printf(\"RemoteStartJobCommand: starting, jobid=%s, clientid=%s\\n\", data.JobId, data.ClientId)\n\tif impl.Router == nil {\n\t\treturn nil, fmt.Errorf(\"cannot start remote job: no router available\")\n\t}\n\n\twshPath, err := impl.getWshPath()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Printf(\"RemoteStartJobCommand: wshPath=%s\\n\", wshPath)\n\n\treadyPipeRead, readyPipeWrite, err := os.Pipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot create ready pipe: %w\", err)\n\t}\n\tdefer readyPipeRead.Close()\n\tdefer readyPipeWrite.Close()\n\n\tcmd := exec.Command(wshPath, \"jobmanager\", \"--jobid\", data.JobId, \"--clientid\", data.ClientId)\n\tif data.PublicKeyBase64 != \"\" {\n\t\tcmd.Env = append(os.Environ(), \"WAVETERM_PUBLICKEY=\"+data.PublicKeyBase64)","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshrpc/wshremote/wshremote_job.go#L118-L154","documentation":"RemoteStartJobCommand requires a wshrouter.Router to route RPC traffic to the spawned job-manager process. When ServerImpl.Router is nil the server has no RPC routing capability, so starting a remote job is impossible and the call fails fast before spawning any process.","triggerScenarios":"Calling RemoteStartJobCommand (or the RPC 'remote:startjob' command) on a ServerImpl that was constructed without assigning its Router field, or after the router was never wired during server initialization.","commonSituations":"Embedding wshremote.ServerImpl in a custom server/daemon and forgetting to set Router; constructing ServerImpl via a zero-value struct literal instead of the normal init path; a router being torn down or not yet initialized when the job start RPC arrives.","solutions":["Set ServerImpl.Router to an initialized wshrouter.Router before serving RPCs","Verify the server initialization path that wires Router actually runs before accepting remote:startjob calls","If Router can be nil legitimately, guard the caller with a check and return a clear configuration error"],"exampleFix":"// before\nimpl := &wshremote.ServerImpl{}\nimpl.RpcClient = client\n// after\nimpl := &wshremote.ServerImpl{}\nimpl.RpcClient = client\nimpl.Router = router // wshrouter.Router must be set before serving","handlingStrategy":"validation","validationCode":"if server.Router == nil {\n    return fmt.Errorf(\"remote job server not initialized: Router is nil\")\n}\n// then call RemoteStartJobCommand","typeGuard":"func routerReady(impl *wshremote.ServerImpl) bool {\n    return impl != nil && impl.Router != nil\n}","tryCatchPattern":"rtn, err := server.RemoteStartJobCommand(ctx, data)\nif err != nil {\n    if strings.Contains(err.Error(), \"no router available\") {\n        // reinitialize server/router wiring before retrying\n    }\n    return err\n}","preventionTips":["Always construct ServerImpl through the init path that sets Router","Add a startup assertion that Router != nil before serving RPCs","Cover server wiring in an integration test that calls remote:startjob"],"tags":["rpc","router","initialization","remote-job"],"backgroundTag":"router-not-initialized","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}