hashicorp/nomad · error

launch not found

Error message

launch not found

What it means

Returned by StateStore.DeletePeriodicLaunchTxn when no PeriodicLaunch record exists for the given (namespace, jobID). The launch bookkeeping (last launched time for a periodic job) is absent, so there is nothing to delete and the caller's intent is invalid.

Source

Thrown at nomad/state/state_store.go:3391

	defer txn.Abort()

	err := s.DeletePeriodicLaunchTxn(index, namespace, jobID, txn)
	if err == nil {
		return txn.Commit()
	}
	return err
}

// DeletePeriodicLaunchTxn is used to delete the periodic launch, like DeletePeriodicLaunch
// but in a transaction.  Useful for when making multiple modifications atomically
func (s *StateStore) DeletePeriodicLaunchTxn(index uint64, namespace, jobID string, txn Txn) error {
	// Lookup the launch
	existing, err := txn.First("periodic_launch", "id", namespace, jobID)
	if err != nil {
		return fmt.Errorf("launch lookup failed: %v", err)
	}
	if existing == nil {
		return fmt.Errorf("launch not found")
	}

	// Delete the launch
	if err := txn.Delete("periodic_launch", existing); err != nil {
		return fmt.Errorf("launch delete failed: %v", err)
	}
	if err := txn.Insert("index", &IndexEntry{"periodic_launch", index}); err != nil {
		return fmt.Errorf("index update failed: %v", err)
	}

	return nil
}

// PeriodicLaunchByID is used to lookup a periodic launch by the periodic job
// ID.
func (s *StateStore) PeriodicLaunchByID(ws memdb.WatchSet, namespace, id string) (*structs.PeriodicLaunch, error) {
	txn := s.db.ReadTxn()

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Treat this as an idempotency signal: if the job is already gone, log and return success instead of erroring
  2. Verify the namespace and job ID match the registered periodic job (use job list / job inspect)
  3. Ensure the job was actually registered as periodic before deleting its launch record
  4. Check for duplicate delete calls in your automation (e.g. retries after a successful delete)

Example fix

// before
if err := state.DeletePeriodicLaunch(100, "default", "my-periodic"); err != nil {
	return err
}
// after
if err := state.DeletePeriodicLaunch(100, "default", "my-periodic"); err != nil {
	if strings.Contains(err.Error(), "launch not found") {
		return nil // already deleted; idempotent
	}
	return err
}
Defensive patterns

Strategy: validation

Validate before calling

launch, err := state.PeriodicLaunchByID(ws, namespace, jobID)
if err != nil { return err }
if launch == nil {
	// already deleted or never launched: skip delete
	return nil
}

Type guard

if l, ok := existing.(*structs.PeriodicLaunch); !ok || l == nil { return nil }

Try / catch

if err := state.DeletePeriodicLaunch(idx, ns, jobID); err != nil {
	if strings.Contains(err.Error(), "launch not found") {
		return nil // idempotent success
	}
	return err
}

Prevention

When it happens

Trigger: Calling DeletePeriodicLaunch(index, namespace, jobID) for a job ID that never had UpsertPeriodicLaunch called, or for a job that was already deleted (or whose launch was already removed), e.g. deleting a periodic job twice.

Common situations: Deregistering a periodic job whose launch entry was already cleaned up by a prior delete or job purge; stale job IDs from an older namespace; calling delete with namespace/jobID swapped.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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