hashicorp/nomad · error

must call StopTask for the given task before Destroy or set

Error message

must call StopTask for the given task before Destroy or set force to true

What it means

DestroyTask refuses to remove a container that is still running unless force=true or StopTask was called first. This is a safety check so Nomad never hard-kills a running workload without a prior stop (or explicit force), since the driver tracks the task lifecycle.

Source

Thrown at drivers/docker/driver.go:1802

	}

	dockerClient, err := d.getDockerClient()
	if err != nil {
		return err
	}

	c, err := dockerClient.ContainerInspect(d.ctx, h.containerID, mclient.ContainerInspectOptions{})
	if err != nil {
		if errdefs.IsNotFound(err) {
			h.logger.Info("container was removed out of band, will proceed with DestroyTask",
				"error", err)
		} else {
			return fmt.Errorf("failed to inspect container state: %v", err)
		}
	} else {
		if c.Container.State.Running {
			if !force {
				return fmt.Errorf("must call StopTask for the given task before Destroy or set force to true")
			}
			if _, err := dockerClient.ContainerStop(d.ctx, h.containerID, mclient.ContainerStopOptions{Timeout: new(0)}); err != nil {
				h.logger.Warn("failed to stop container during destroy", "error", err)
			}
		}

		if h.removeContainerOnExit {
			if _, err := dockerClient.ContainerRemove(d.ctx, h.containerID, mclient.ContainerRemoveOptions{Force: true}); err != nil {
				h.logger.Error("error removing container", "error", err)
			}
		} else {
			h.logger.Debug("not removing container due to config")
		}
	}

	if err := d.cleanupImage(h); err != nil {
		h.logger.Error("failed to cleanup image after destroying container",
			"error", err)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Call StopTask on the task handle before DestroyTask.
  2. Pass force=true to DestroyTask if you intentionally want the running container stopped and removed (the driver will ContainerStop it first).
  3. Fix orchestration code so the standard Stop → Destroy lifecycle is respected.

Example fix

// before
h.DestroyTask(context.Background(), false) // container still running
// after
h.StopTask(context.Background(), time.Second*30)
h.DestroyTask(context.Background(), false)
// or: h.DestroyTask(ctx, true) to force-stop
Defensive patterns

Strategy: try-catch

Validate before calling

// check running state before destroy
inspected, err := client.ContainerInspect(ctx, containerID, mclient.ContainerInspectOptions{})
if err == nil && inspected.Container.State.Running {
    client.ContainerStop(ctx, containerID, mclient.ContainerStopOptions{Timeout: new(uint32)})
}

Try / catch

err := h.DestroyTask(ctx, false)
if err != nil && strings.Contains(err.Error(), "must call StopTask") {
    h.StopTask(ctx, 30*time.Second)
    err = h.DestroyTask(ctx, false)
}

Prevention

When it happens

Trigger: Calling driver DestroyTask (or internal destroy APIs) directly on a handle whose container still reports State.Running=true, without having invoked StopTask and without the force flag.

Common situations: Custom tooling/plugins driving the driver API directly out of order; scripting against Nomad internals during GC tests; writing driver tests that skip the Stop step.

Related errors


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