{"record":{"id":"23923b380117ae8a","repo":"hashicorp/nomad","slug":"missing-job-id-for-marking-job-as-stable","errorCode":null,"errorMessage":"missing job ID for marking job as stable","messagePattern":"missing job ID for marking job as stable","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/job_endpoint.go","lineNumber":646,"sourceCode":"\tj.srv.MeasureRPCRate(\"job\", structs.RateMetricWrite, args)\n\tif authErr != nil {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\tdefer metrics.MeasureSince([]string{\"nomad\", \"job\", \"stable\"}, time.Now())\n\n\t// Check for read-job permissions\n\tif aclObj, err := j.srv.ResolveACL(args); err != nil {\n\t\treturn err\n\t} else if !aclObj.AllowNsOpAnyOf(args.RequestNamespace(),\n\t\tacl.NamespaceCapabilitySubmitJob,\n\t\tacl.NamespaceCapabilityStableJob,\n\t) {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\n\t// Validate the arguments\n\tif args.JobID == \"\" {\n\t\treturn fmt.Errorf(\"missing job ID for marking job as stable\")\n\t}\n\n\t// Lookup the job by version\n\tsnap, err := j.srv.fsm.State().Snapshot()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tws := memdb.NewWatchSet()\n\tjobV, err := snap.JobByIDAndVersion(ws, args.RequestNamespace(), args.JobID, args.JobVersion)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif jobV == nil {\n\t\treturn fmt.Errorf(\"job %q in namespace %q at version %d not found\", args.JobID, args.RequestNamespace(), args.JobVersion)\n\t}\n\n\t// Commit this stability request via Raft","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/job_endpoint.go#L628-L664","documentation":"The Stable RPC marks a deployment of a specific job version as stable. Its argument validation requires a job ID; when args.JobID is the empty string it returns \"missing job ID for marking job as stable\" before touching the state store. This is pure input validation of the JobStableRequest.","triggerScenarios":"Calling the Job.Stable RPC (POST /v1/job/<id>/stable) with a JobStableRequest whose JobID field is unset/empty — typically building the request programmatically and forgetting to set JobID, or deserializing a request where the ID was dropped.","commonSituations":"Hand-rolled API calls or automation constructing JobStableRequest structs; templates/CI scripts where the job ID variable interpolates to empty; SDK wrappers that fail to propagate the ID parameter.","solutions":["Set JobID on the JobStableRequest before calling the RPC","Validate the job ID is non-empty in your tooling before issuing the request","If the ID comes from a variable/config, log it and fix the interpolation or config source","Use the CLI (`nomad job eval`-adjacent workflows or deployment tooling) which fills the ID automatically"],"exampleFix":"// before\nargs := &nomad.JobStableRequest{JobVersion: 5, Stable: true}\n// after\nargs := &nomad.JobStableRequest{JobID: \"webapp\", JobVersion: 5, Stable: true}","handlingStrategy":"validation","validationCode":"if jobID == \"\" {\n    return fmt.Errorf(\"refusing to call Stable RPC: empty job ID\")\n}","typeGuard":"func validJobStableRequest(a *api.JobStableRequest) bool { return a != nil && a.JobID != \"\" }","tryCatchPattern":"_, _, err := client.Jobs().Stable(args, wo)\nif err != nil && strings.Contains(err.Error(), \"missing job ID\") {\n    // request was built without JobID: fix request construction\n}","preventionTips":["Construct JobStableRequest via a helper that requires the job ID parameter","Fail fast on empty identifiers at the edges of your automation","Check that config/variables feeding the job ID are non-empty before API calls"],"tags":["nomad","job-stable","validation","missing-argument"],"backgroundTag":"missing-required-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"}