{"record":{"id":"49b31cbb4e9d4955","repo":"wavetermdev/waveterm","slug":"jobkind-must-be-q-or-q","errorCode":null,"errorMessage":"jobkind must be %q or %q","messagePattern":"jobkind must be %q or %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/jobcontroller/jobcontroller.go","lineNumber":618,"sourceCode":"\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\")\n\t}\n\tif params.JobKind != JobKind_Shell && params.JobKind != JobKind_Task {\n\t\treturn \"\", fmt.Errorf(\"jobkind must be %q or %q\", JobKind_Shell, JobKind_Task)\n\t}\n\tif params.Cmd == \"\" {\n\t\treturn \"\", fmt.Errorf(\"command is required\")\n\t}\n\tif params.TermSize == nil {\n\t\tparams.TermSize = &waveobj.TermSize{Rows: 24, Cols: 80}\n\t}\n\n\tisConnected, err := conncontroller.IsConnected(params.ConnName)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"error checking connection status: %w\", err)\n\t}\n\tif !isConnected {\n\t\treturn \"\", fmt.Errorf(\"connection %q is not connected\", params.ConnName)\n\t}\n\n\tjobId := uuid.New().String()\n\tjobAuthToken, err := utilfn.RandomHexString(32)","sourceCodeStart":600,"sourceCodeEnd":636,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobcontroller/jobcontroller.go#L600-L636","documentation":"StartJob validates params.JobKind before doing any work and rejects anything that is not exactly JobKind_Shell (\"shell\") or JobKind_Task (\"task\"). The %q verbs wrap the valid constants in quotes so the developer can see the exact accepted values. This is a programming error by the caller, not a runtime/environment failure.","triggerScenarios":"Calling StartJob (directly or via StartRemoteShellJob / JobControllerStartJobCommand) with StartJobParams.JobKind set to an empty string, a typo like \"shel\", a different casing (\"Shell\"), or a stale/removed job-kind constant.","commonSituations":"Hand-constructed StartJobParams in scripts or tests, enum values renamed in a Wave Terminal version upgrade, frontend sending an unvalidated job kind string through an RPC command.","solutions":["Set params.JobKind to jobcontroller.JobKind_Shell for interactive shells or jobcontroller.JobKind_Task for one-shot commands — never a raw string literal.","If the value comes from user input or config, validate it against the two constants before calling StartJob.","Check for a type mismatch after upgrading Wave Terminal (constant values/names may have changed)."],"exampleFix":"// before\nparams := jobcontroller.StartJobParams{ConnName: conn, JobKind: \"shell\", Cmd: \"ls\"}\n// after\nparams := jobcontroller.StartJobParams{ConnName: conn, JobKind: jobcontroller.JobKind_Shell, Cmd: \"ls\"}","handlingStrategy":"validation","validationCode":"if params.JobKind != jobcontroller.JobKind_Shell && params.JobKind != jobcontroller.JobKind_Task {\n    return fmt.Errorf(\"JobKind must be JobKind_Shell or JobKind_Task, got %q\", params.JobKind)\n}","typeGuard":"func isValidJobKind(k string) bool {\n    return k == jobcontroller.JobKind_Shell || k == jobcontroller.JobKind_Task\n}","tryCatchPattern":null,"preventionTips":["Always use the exported JobKind_* constants, never string literals.","Add a compile-time map of allowed job kinds and select from it in UI/config layers.","Validate user/config-supplied kind strings at the boundary before constructing StartJobParams."],"tags":["validation","argument-error","jobcontroller"],"backgroundTag":"invalid-enum-value","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}