{"record":{"id":"aaf8aec0461896c7","repo":"caddyserver/caddy","slug":"caddy-process-exited-with-error-v","errorCode":null,"errorMessage":"caddy process exited with error: %v","messagePattern":"caddy process exited with error: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"cmd/commandfuncs.go","lineNumber":166,"sourceCode":"\t\t\t\tbreak\n\t\t\t}\n\t\t\tlog.Println(err)\n\t\t}\n\t}()\n\n\t// in another goroutine, we await the failure of the child process\n\tgo func() {\n\t\terr := cmd.Wait() // don't send on this line! Wait blocks, but send starts before it unblocks\n\t\texit <- err       // sending on separate line ensures select won't trigger until after Wait unblocks\n\t}()\n\n\t// when one of the goroutines unblocks, we're done and can exit\n\tselect {\n\tcase <-success:\n\t\tfmt.Printf(\"Successfully started Caddy (pid=%d) - Caddy is running in the background\\n\", cmd.Process.Pid)\n\tcase err := <-exit:\n\t\treturn caddy.ExitCodeFailedStartup,\n\t\t\tfmt.Errorf(\"caddy process exited with error: %v\", err)\n\t}\n\n\treturn caddy.ExitCodeSuccess, nil\n}\n\ntype tcpListenFunc func(network, address string) (net.Listener, error)\n\nfunc listenTCPForPingback(listen tcpListenFunc) (net.Listener, error) {\n\tln, ipv4Err := listen(\"tcp4\", \"127.0.0.1:0\")\n\tif ipv4Err == nil {\n\t\treturn ln, nil\n\t}\n\n\tln, ipv6Err := listen(\"tcp6\", \"[::1]:0\")\n\tif ipv6Err == nil {\n\t\treturn ln, nil\n\t}\n","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/cmd/commandfuncs.go#L148-L184","documentation":"Returned by `caddy start` when the spawned child process exits before dialing back the success listener. The parent selects between the pingback channel and cmd.Wait(); if the child dies first (bad config, invalid flags, missing permissions), its exit error is surfaced here. This is the primary 'caddy start failed' signal and almost always means the config or flags are invalid.","triggerScenarios":"`caddy start --config bad-Caddyfile` where the child fails to load config; passing an adapter name not compiled in; child cannot bind the configured ports (permission denied on :80/:443 without privileges, or ports in use); invalid admin endpoint address.","commonSituations":"Starting Caddy on ports <1024 as non-root; another process already on :80/:443; typos in the Caddyfile; --adapter jsonfile unavailable in a custom build; systemd unit passing conflicting flags.","solutions":["Run `caddy run --config <file> --adapter <name>` in the foreground — it shows the child's real error output directly","Check the child's stderr that `caddy start` already passes through (cmd.Stderr = os.Stderr)","Verify ports: ss -ltnp | grep -E ':80|:443' and free them or adjust the config","caddy validate --config <file> to catch config errors before starting"],"exampleFix":"# before\ncaddy start --config Caddyfile  # caddy process exited with error: ... listen tcp :80: bind: permission denied\n# after\nsudo setcap 'cap_net_bind_service=+ep' $(which caddy) && caddy start --config Caddyfile","handlingStrategy":"validation","validationCode":"// catch config/port problems BEFORE caddy start\nif out, err := exec.Command(\"caddy\", \"validate\", \"--config\", cfg).CombinedOutput(); err != nil {\n    return fmt.Errorf(\"config invalid, refusing to start: %s\", out)\n}\nfor _, p := range []string{\":80\", \":443\"} {\n    if ln, err := net.Listen(\"tcp\", p); err != nil {\n        return fmt.Errorf(\"port %s unavailable: %w\", p, err)\n    } else { ln.Close() }\n}","typeGuard":null,"tryCatchPattern":"_, err := caddycmd.Start(fl)\nif err != nil && strings.Contains(err.Error(), \"caddy process exited with error\") {\n    // child's stderr was inherited and printed above it — read that output for root cause\n    // then fix config/flags and retry once; repeated retries without changes will keep failing\n}","preventionTips":["Always caddy validate before caddy start in scripts","Grant CAP_NET_BIND_SERVICE (setcap or AmbientCapabilities=) for ports <1024","Prefer `caddy run` under systemd so failures surface directly in journalctl"],"tags":["cli","caddy-start","config","ports","startup"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}