docker/compose · error

exit code %d

Error message

exit code %d

What it means

After a `sync_exec` watch trigger copies files into the container, Compose runs the configured command via the Docker exec API and inspects its result. This error means the exec'd process finished with a non-zero exit code — the command itself failed inside the container.

Source

Thrown at pkg/compose/watch.go:518

		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 {
	var (
		restart   = map[string]bool{}
		syncfiles = map[string][]*sync.PathMapping{}

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Run the same exec command manually (`docker compose exec <svc> <cmd>`) to see the real failure output
  2. Fix the underlying command failure (build error, missing tool, bad path)
  3. Make the exec command resilient (e.g. a wrapper script that tolerates transient failures) if you don't want watch to stop
Defensive patterns

Strategy: try-catch

Try / catch

// When programmatically driving watch sync_exec, treat non-zero exits as command failures
err := watcher.SyncAndExec(ctx, svc, cmd)
var exitErr *exec.ExitError // or string-match "exit code %d" from compose
if err != nil && strings.Contains(err.Error(), "exit code") {
    log.Printf("watch exec failed (command exited non-zero): %v", err)
    // keep watching; don't crash the loop
}

Prevention

When it happens

Trigger: A file change fires a sync_exec rule; Compose creates an exec, waits for it, calls `ExecInspect`, and `ExitCode != 0`. Typical when the command is a test/build step that fails on the newly synced code.

Common situations: Running `make test` or a reloader command as the exec and the code change breaks the build; the command references a binary missing from the image's PATH; the command's working directory assumptions don't hold in the container.

Related errors


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