docker/cli · error
cannot attach to a stopped container, start it first
Error message
cannot attach to a stopped container, start it first
What it means
inspectContainerAndCheckState (cli/command/container/attach.go:25), used by `docker attach`, inspects the target container and at line 30-31 returns errors.New("cannot attach to a stopped container, start it first") when c.Container.State.Running is false. Attaching requires a live process to stream I/O from, so a stopped/exited container is rejected client-side before the attach API call.
Solutions
- Start the container first: `docker start <id>` then `docker attach <id>`.
- Use `docker ps -a` to confirm the container's STATUS; if Exited, inspect logs (`docker logs <id>`) to see why it stopped.
- For one-shot processes, reconsider attach — use `docker run` (foreground) or `docker logs` instead.
- If it exits immediately, fix the entrypoint/command and recreate the container.
Example fix
# before (container is stopped) docker attach web # after docker start web && docker attach web
Defensive patterns
Strategy: validation
Validate before calling
// Check container state before attempting attach:
c, err := cli.Client().ContainerInspect(ctx, id, client.ContainerInspectOptions{})
if err != nil { return err }
if !c.State.Running {
return fmt.Errorf("container %s is not running; start it first", id)
} Try / catch
if _, err := inspectContainerAndCheckState(ctx, cli.Client(), id); err != nil {
if strings.Contains(err.Error(), "cannot attach to a stopped container") {
// start the container, then retry attach
}
return err
} Prevention
- Verify STATUS with `docker ps` before attach.
- For one-shot workloads, prefer `docker run` over attach.
- Check `docker logs` if the container exited unexpectedly.
When it happens
Trigger: Running `docker attach <id>` against a container whose state is 'exited', 'created', 'dead', or otherwise not Running; attaching to a container that crashed/exited between your check and the call.
Common situations: Container exited immediately (bad entrypoint, missing args); short-lived foreground process already finished; attaching to a container started with -d that has since stopped; race where the container exits right before attach.
Related errors
- cannot attach to a paused container, unpause it first
- cannot attach to a restarting container, wait until it is…
- source can not be empty
- destination can not be empty
- must specify at least one container source
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/08e2cc79a96600e1.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/container/attach.go:31
"github.com/moby/sys/signal"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// AttachOptions group options for `attach` command
type AttachOptions struct {
NoStdin bool
Proxy bool
DetachKeys string
}
func inspectContainerAndCheckState(ctx context.Context, apiClient client.APIClient, args string) (*container.InspectResponse, error) {
c, err := apiClient.ContainerInspect(ctx, args, client.ContainerInspectOptions{})
if err != nil {
return nil, err
}
if !c.Container.State.Running {
return nil, errors.New("cannot attach to a stopped container, start it first")
}
if c.Container.State.Paused {
return nil, errors.New("cannot attach to a paused container, unpause it first")
}
if c.Container.State.Restarting {
return nil, errors.New("cannot attach to a restarting container, wait until it is running")
}
return &c.Container, nil
}
// newAttachCommand creates a new cobra.Command for `docker attach`
func newAttachCommand(dockerCLI command.Cli) *cobra.Command {
var opts AttachOptions
cmd := &cobra.Command{
Use: "attach [OPTIONS] CONTAINER",
Short: "Attach local standard input, output, and error streams to a running container",View on GitHub (pinned to 4f84911bfe)