{"record":{"id":"1070da423b9ce9ba","repo":"hashicorp/nomad","slug":"must-pass-non-nil-job","errorCode":null,"errorMessage":"must pass non-nil job","messagePattern":"must pass non-nil job","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/jobs.go","lineNumber":502,"sourceCode":"\t\treturn \"\", nil, err\n\t}\n\treturn resp.EvalID, wm, nil\n}\n\n// PlanOptions is used to pass through job planning parameters\ntype PlanOptions struct {\n\tDiff           bool\n\tPolicyOverride bool\n}\n\nfunc (j *Jobs) Plan(job *Job, diff bool, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) {\n\topts := PlanOptions{Diff: diff}\n\treturn j.PlanOpts(job, &opts, q)\n}\n\nfunc (j *Jobs) PlanOpts(job *Job, opts *PlanOptions, q *WriteOptions) (*JobPlanResponse, *WriteMeta, error) {\n\tif job == nil {\n\t\treturn nil, nil, errors.New(\"must pass non-nil job\")\n\t}\n\tif job.ID == nil {\n\t\treturn nil, nil, errors.New(\"job is missing ID\")\n\t}\n\n\t// Setup the request\n\treq := &JobPlanRequest{\n\t\tJob: job,\n\t}\n\tif opts != nil {\n\t\treq.Diff = opts.Diff\n\t\treq.PolicyOverride = opts.PolicyOverride\n\t}\n\n\tvar resp JobPlanResponse\n\twm, err := j.client.put(\"/v1/job/\"+url.PathEscape(*job.ID)+\"/plan\", req, &resp, q)\n\tif err != nil {\n\t\treturn nil, nil, err","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/api/jobs.go#L484-L520","documentation":"Guard in Jobs.PlanOpts: the job planning API requires a non-nil *Job; calling Plan/PlanOpts with a nil job pointer is a client programming error, so the request never reaches the server.","triggerScenarios":"Calling j.PlanOpts(nil, opts, q) directly, or calling j.Plan(job, diff, q) where job was produced by an earlier call that returned nil on a handled error path.","commonSituations":"Structuring config where the job is conditionally built and a nil slips through; parsing a job spec file that failed silently; refactoring that removed an earlier nil check.","solutions":["Check job != nil before calling PlanOpts or Plan","Ensure the code path that constructs the *Job always returns a valid job or an error","If loading from file, use api.Parse and stop on its error before planning"],"exampleFix":"// before\nj.PlanOpts(job, &opts, q)\n// after\nif job == nil {\n    return fmt.Errorf(\"cannot plan: job is nil\")\n}\nj.PlanOpts(job, &opts, q)","handlingStrategy":"validation","validationCode":"func canPlan(job *api.Job) bool { return job != nil }","typeGuard":"func jobIsNonNil(j *api.Job) (*api.Job, bool) {\n    if j == nil { return nil, false }\n    return j, true\n}","tryCatchPattern":"resp, _, err := jobs.PlanOpts(job, &opts, q)\nif err != nil {\n    return fmt.Errorf(\"plan failed: %w\", err)\n}","preventionTips":["Nil-check every *Job coming from conditional construction paths","Treat 'job loaded or error' as exclusive outcomes; never continue with a nil job","Use api.Parse and abort on its error before planning"],"tags":["go","nomad","jobs","nil-check","client-validation"],"backgroundTag":"nil-argument","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}