{"record":{"id":"da86e607251f98e8","repo":"docker/compose","slug":"exit-code-d","errorCode":null,"errorMessage":"exit code %d","messagePattern":"exit code (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/compose/watch.go","lineNumber":518,"sourceCode":"\t\treturn err\n\t}\n\n\t// although the errgroup is not tied directly to the context, the operations\n\t// in it are reading/writing to the connection, which is tied to the context,\n\t// so they won't block indefinitely\n\tif err := eg.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\texecResult, err := t.s.apiClient().ExecInspect(ctx, execCreateResp.ID, client.ExecInspectOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif execResult.Running {\n\t\treturn errors.New(\"process still running\")\n\t}\n\tif execResult.ExitCode != 0 {\n\t\treturn fmt.Errorf(\"exit code %d\", execResult.ExitCode)\n\t}\n\treturn nil\n}\n\nfunc (t tarDockerClient) Untar(ctx context.Context, id string, archive io.ReadCloser) error {\n\t_, err := t.s.apiClient().CopyToContainer(ctx, id, client.CopyToContainerOptions{\n\t\tDestinationPath: \"/\",\n\t\tContent:         archive,\n\t\tCopyUIDGID:      true,\n\t})\n\treturn err\n}\n\n//nolint:gocyclo\nfunc (s *composeService) handleWatchBatch(ctx context.Context, project *types.Project, options api.WatchOptions, batch []watch.FileEvent, rules []watchRule, syncer sync.Syncer) error {\n\tvar (\n\t\trestart   = map[string]bool{}\n\t\tsyncfiles = map[string][]*sync.PathMapping{}","sourceCodeStart":500,"sourceCodeEnd":536,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/pkg/compose/watch.go#L500-L536","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run the same exec command manually (`docker compose exec <svc> <cmd>`) to see the real failure output","Fix the underlying command failure (build error, missing tool, bad path)","Make the exec command resilient (e.g. a wrapper script that tolerates transient failures) if you don't want watch to stop"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// When programmatically driving watch sync_exec, treat non-zero exits as command failures\nerr := watcher.SyncAndExec(ctx, svc, cmd)\nvar exitErr *exec.ExitError // or string-match \"exit code %d\" from compose\nif err != nil && strings.Contains(err.Error(), \"exit code\") {\n    log.Printf(\"watch exec failed (command exited non-zero): %v\", err)\n    // keep watching; don't crash the loop\n}","preventionTips":["Test the exec command inside the image before wiring it into watch rules","Use idempotent commands (reloader, make watch) that tolerate repeated runs","Ensure binaries referenced by exec.command exist in the image's PATH"],"tags":["compose","watch","exec","exit-code"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}