docker/compose · error

process still running

Error message

process still running

What it means

After a sync_exec watch action streams the exec session to completion, Compose inspects the exec to fetch its exit code. If ExecInspect still reports Running=true at that point, the exec process never terminated, which would leave the sync step in an ambiguous state, so it errors with 'process still running'.

Source

Thrown at pkg/compose/watch.go:515

		Detach: false,
	})
	if err != nil {
		return err
	}

	// although the errgroup is not tied directly to the context, the operations
	// in it are reading/writing to the connection, which is tied to the context,
	// so they won't block indefinitely
	if err := eg.Wait(); err != nil {
		return err
	}

	execResult, err := t.s.apiClient().ExecInspect(ctx, execCreateResp.ID, client.ExecInspectOptions{})
	if err != nil {
		return err
	}
	if execResult.Running {
		return errors.New("process still running")
	}
	if execResult.ExitCode != 0 {
		return fmt.Errorf("exit code %d", execResult.ExitCode)
	}
	return nil
}

func (t tarDockerClient) Untar(ctx context.Context, id string, archive io.ReadCloser) error {
	_, err := t.s.apiClient().CopyToContainer(ctx, id, client.CopyToContainerOptions{
		DestinationPath: "/",
		Content:         archive,
		CopyUIDGID:      true,
	})
	return err
}

//nolint:gocyclo
func (s *composeService) handleWatchBatch(ctx context.Context, project *types.Project, options api.WatchOptions, batch []watch.FileEvent, rules []watchRule, syncer sync.Syncer) error {

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Make the sync_exec command finite: ensure it runs to completion and exits (no servers, no tail -f)
  2. Background or daemonize inside the container yourself if you need a long-running process, and let the exec command return immediately
  3. If it looks like a transient race, simply retry the watch — the container state is not corrupted

Example fix

# before
- action: sync_exec
  path: ./src
  target: /app
  exec:
    command: npm run dev   # never exits
# after
- action: sync_exec
  path: ./src
  target: /app
  exec:
    command: sh -c "npm install && npm run build"
Defensive patterns

Strategy: retry

Validate before calling

docker compose exec <svc> sh -c 'command -v my-one-shot-cmd >/dev/null && my-one-shot-cmd --check'

Try / catch

# transient race retry: re-running watch re-runs sync_exec
for i in 1 2 3; do docker compose watch & sleep 5; kill %1 2>/dev/null; done

Prevention

When it happens

Trigger: A develop.watch rule with action: sync_exec whose command is long-running or daemonizing (e.g. a server, tail -f, or a script that backgrounds itself), so the attach streams finish but the exec stays alive.

Common situations: Using sync_exec to run build/test commands that spawn daemons; commands like `npm run dev` that never exit; a race where a slow-starting process has not exited when inspected.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/33e42fd768c07a15. Report an issue: GitHub.