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

  1. Pass the job's ID explicitly (sub.JobID or job.ID) when calling the state store setter
  2. Validate jobID != "" before invoking the write; fail fast at the caller
  3. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/7dfb2e11b49536e4. Report an issue: GitHub.