{"record":{"id":"9d0a9c55af74f97d","repo":"wavetermdev/waveterm","slug":"failed-to-start-durable-shell-w","errorCode":null,"errorMessage":"failed to start durable shell: %w","messagePattern":"failed to start durable shell: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/blockcontroller/durableshellcontroller.go","lineNumber":274,"sourceCode":"\t\tBlockId:   dsc.BlockId,\n\t\tConn:      connName,\n\t}\n\tjwtStr, err := wshutil.MakeClientJWTToken(rpcContext)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"error making jwt token: %w\", err)\n\t}\n\tswapToken.RpcContext = &rpcContext\n\tswapToken.Env[wshutil.WaveJwtTokenVarName] = jwtStr\n\tcmdOpts := shellexec.CommandOptsType{\n\t\tInteractive: true,\n\t\tLogin:       true,\n\t\tCwd:         cwd,\n\t\tSwapToken:   swapToken,\n\t\tForceJwt:    blockMeta.GetBool(waveobj.MetaKey_CmdJwt, false),\n\t}\n\tjobId, err := shellexec.StartRemoteShellJob(ctx, ctx, termSize, cmdStr, cmdOpts, conn, dsc.BlockId)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to start durable shell: %w\", err)\n\t}\n\treturn jobId, nil\n}\n","sourceCodeStart":256,"sourceCodeEnd":278,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/blockcontroller/durableshellcontroller.go#L256-L278","documentation":"This is the final wrapper around shellexec.StartRemoteShellJob, which actually spawns the durable remote shell job over the connection. Any failure launching that job (SSH exec failure, remote command error, job setup failure) is surfaced with this message. The durable shell was not created and no JobId is returned.","triggerScenarios":"StartRemoteShellJob fails: the SSH channel cannot open, the remote shell command (cmdStr) fails, term/command options are invalid, or the remote refuses the interactive shell session.","commonSituations":"Remote user's default shell is invalid or restricted (rbash, nologin); server rejects interactive/login shells (MaxSessions, forced command in authorized_keys); disk-full or permission errors on the remote; cmd meta specifies a binary not present remotely.","solutions":["Read the wrapped inner error for the precise remote-side failure.","Test the same shell command over plain ssh (ssh host -t 'bash -l').","Check the remote account's shell and authorized_keys for forced commands restricting exec.","Verify the cmd/cmd-cwd meta values exist on the remote.","Retry after fixing remote capacity issues (disk, sessions, permissions)."],"exampleFix":"// before\nmeta := waveobj.MetaMapType{\"cmd\": \"/usr/local/bin/zsh\", \"conn\": \"server\"} // zsh not installed remotely\n// after\nmeta := waveobj.MetaMapType{\"cmd\": \"/bin/bash\", \"conn\": \"server\"} // shell that exists on the remote","handlingStrategy":"try-catch","validationCode":"// sanity-check meta before Start\nif cmd := blockMeta.GetString(waveobj.MetaKey_Cmd, \"\"); cmd != \"\" {\n    // ensure cmd exists remotely via a probe command if possible\n}\nif conn.DeriveConnStatus().Status != conncontroller.Status_Connected {\n    return fmt.Errorf(\"remote not connected\")\n}","typeGuard":null,"tryCatchPattern":"if err := controller.Start(ctx, meta, rtOpts, false); err != nil {\n    var inner error\n    errors.As(err, &inner) // unwrap 'failed to start durable shell' cause\n    log.Printf(\"durable shell start failed: %v\", inner)\n    return err\n}","preventionTips":["Verify the remote account's login shell and authorized_keys are not restricted","Test the cmd value over plain ssh first","Keep remote disks/permissions healthy","Match wsh versions between client and remote"],"tags":["ssh","remote","shell","process","spawn"],"backgroundTag":"remote-shell-start-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}