hashicorp/nomad · error
job_submission requires a jobID
Error message
job_submission requires a jobID
What it means
Internal invariant guard in StateStore.updateJobSubmission: the function was invoked with an empty jobID. Storing a job's original HCL submission requires the namespace/jobID/version key; a missing jobID means the caller (job registration path) passed invalid state-store arguments.
Source
Thrown at nomad/state/state_store.go:5799
return nil
}
// updateJobSubmission stores the original job source and variables associated that the
// job structure originates from. It is up to the job submitter to include the source
// material, and as such sub may be nil, in which case nothing is stored.
func (s *StateStore) updateJobSubmission(index uint64, sub *structs.JobSubmission, namespace, jobID string, version uint64, txn *txn) error {
// critical that we operate on a copy; the original must not be modified
// e.g. in the case of job gc and its last second version bump
sub = sub.Copy()
switch {
case sub == nil:
return nil
case namespace == "":
return errors.New("job_submission requires a namespace")
case jobID == "":
return errors.New("job_submission requires a jobID")
default:
sub.Namespace = namespace
sub.JobID = jobID
sub.JobModifyIndex = index
sub.Version = version
}
// check if we already have a submission for this (namespace, jobID, version)
obj, err := txn.First("job_submission", "id", namespace, jobID, version)
if err != nil {
return err
}
if obj != nil {
// if we already have a submission for this (namespace, jobID, version)
// then there is nothing to do; manually avoid potential for duplicates
return nil
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Pass the job's ID explicitly (sub.JobID or job.ID) when calling the state store setter
- Validate jobID != "" before invoking the write; fail fast at the caller
- If the job ID comes from decoded input, canonicalize the Job struct first so ID is populated
Example fix
// before
stateStore.UpsertJobSubmission(msgType, idx, txn, sub, ns, "", index, version)
// after
if jobID == "" { jobID = sub.JobID }
if jobID == "" { return errors.New("jobID required") }
stateStore.UpsertJobSubmission(msgType, idx, txn, sub, ns, jobID, index, version) Defensive patterns
Strategy: validation
Validate before calling
if jobID == "" {
if sub != nil { jobID = sub.JobID }
if jobID == "" { return errors.New("jobID is required for job_submission") }
} Type guard
func hasJobID(sub *structs.JobSubmission) bool {
return sub != nil && sub.JobID != ""
} Try / catch
if err := upsertSubmission(sub, ns, jobID, idx); err != nil && strings.Contains(err.Error(), "requires a jobID") {
return fmt.Errorf("submission write dropped: ensure job.ID is set before upsert: %w", err)
} Prevention
- Validate job.ID is populated right after decoding request payloads
- Pass the Job struct's ID directly instead of separate (possibly empty) variables
- Add unit tests asserting submission writes always carry jobID
When it happens
Trigger: Calling the job submission state-store setter with jobID="" — e.g. writing a submission object detached from its parent job, or a caller that has the submission struct but lost the job ID variable (empty string from parsing/decoding failure).
Common situations: Embedded/custom writers that call the state store directly; deserialization bugs where job.ID is empty; refactored code paths where the job ID argument is accidentally passed as an empty string.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- job_submission requires a namespace
- validating volume %q against state failed: %v
- unable to update job submission: %v
- job submission lookup failed: %v
- unknown new job status %q
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/7dfb2e11b49536e4.
Report an issue: GitHub.