hashicorp/nomad · error
scheduler resulted in an unexpected number of plans: %v
Error message
scheduler resulted in an unexpected number of plans: %v
What it means
Job.Plan ran the scheduler worker for the evaluation, expecting exactly one scheduling plan back, but got a different count. A count of 0 usually means the scheduler produced no plan for the proposed job; >1 indicates an internal scheduler invariant violation. The endpoint aborts annotation and returns the count.
Source
Thrown at nomad/job_endpoint.go:1908
// Create an in-memory Planner that returns no errors and stores the
// submitted plan and created evals.
planner := &sstructs.PlanBuilder{
State: &snap.StateStore,
}
// Create the scheduler and run it
sched, err := scheduler.NewScheduler(eval.Type, j.logger, j.srv.workersEventCh, snap, planner)
if err != nil {
return err
}
if err := sched.Process(eval); err != nil {
return err
}
// Annotate and store the diff
if plans := len(planner.Plans); plans != 1 {
return fmt.Errorf("scheduler resulted in an unexpected number of plans: %v", plans)
}
annotations := planner.Plans[0].Annotations
if args.Diff {
jobDiff, err := existingJob.Diff(args.Job, true)
if err != nil {
return fmt.Errorf("failed to create job diff: %v", err)
}
if err := scheduler.Annotate(jobDiff, annotations); err != nil {
return fmt.Errorf("failed to annotate job diff: %v", err)
}
reply.Diff = jobDiff
}
// Grab the failures
if len(planner.Evals) != 1 {
return fmt.Errorf("scheduler resulted in an unexpected number of eval updates: %v", planner.Evals)
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Inspect server logs around the plan request for scheduler errors explaining why no plan was produced.
- Retry the plan request; transient worker issues can yield zero plans.
- Validate the job type and placement constraints ('nomad job validate') to ensure a scheduler can process it.
- If reproducible (especially plans > 1), capture a nomad operator debug bundle and file a bug — this path should be invariant.
Defensive patterns
Strategy: retry
Validate before calling
// Ensure the job type is schedulable before planning
if *job.Type == "system" || *job.Type == "batch" {
// verify constraints/placeability with validate first
if _, _, err := client.Jobs().Validate(job, nil); err != nil { return err }
} Try / catch
plan, _, err := client.Jobs().Plan(job, true, nil)
if err != nil && strings.Contains(err.Error(), "unexpected number of plans") {
// one retry after brief backoff; persistent counts (>1) indicate a Nomad bug — capture a debug bundle
} Prevention
- Retry plan requests once on transient scheduler failures.
- Keep agents/servers on matching Nomad versions to avoid plan-submission drift.
- Watch server logs for scheduler worker errors during plan testing.
When it happens
Trigger: Job.Plan where after sched.Process(eval), len(planner.Plans) != 1 at nomad/job_endpoint.go:1908 — commonly 0 plans when the planner's SubmitPlan never fired for the fake eval created from args.Job.
Common situations: Planning a job whose constraints/placeability produce no eval work in that scheduler (e.g. system-job edge cases); scheduler worker errors swallowed upstream; Nomad bugs after version upgrades changing plan submission behavior; job type mapping to an unexpected scheduler.
Related errors
- plan exceeds max allocation
- missing state refresh after partial commit
- eval broker is enabled; eval broker must be paused to delete
- eval broker is enabled; eval broker must be paused to delete
- Job registration, dispatch, and scale are disabled by the sc
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/67761403254fa0c2.
Report an issue: GitHub.