charmbracelet/crush · error

failed to detach crush server process

Error message

failed to detach crush server process: %v

What it means

Wraps the error from os/exec's Process.Release() after successfully starting the detached server. Release detaches the parent from the child so it won't be reaped; failure usually means the child already exited and was reaped, or the process handle is invalid on this platform.

Solutions

  1. Check the server's stderr log file for the real startup failure before Release
  2. Verify server port/config so the child survives long enough to be released
  3. Treat Release failure as non-fatal if the readiness probe confirms the server is healthy
  4. Retry spawnAndWaitReady once, since the failure can be a transient reaping race

Example fix

// before: abort on any Release error
if err := c.Process.Release(); err != nil {
	return fmt.Errorf("failed to detach crush server process: %v", err)
}
// after: log but continue if readiness probe succeeds
if err := c.Process.Release(); err != nil {
	slog.Warn("Failed to detach crush server process", "error", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// No practical pre-call validation; verify child health around Release.
ready := waitForServerReady(addr, 5*time.Second)

Try / catch

if err := startDetachedServer(cmd); err != nil {
	if strings.Contains(err.Error(), "detach") && isServerHealthy(addr) {
		slog.Warn("Detach failed but server is healthy; continuing")
		return nil
	}
	return err
}

Prevention

When it happens

Trigger: startDetachedServer calls c.Process.Release() after c.Start(); the child exited immediately after start (crash, port conflict, bad config), or the OS already reaped it, making the handle stale.

Common situations: Server crashes on startup so the process is gone by Release time; sandboxed environments limiting process APIs; race between a supervisor and Release.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/0837197c68953fc7. Report an issue: GitHub.

Appendix: source

Thrown at internal/cmd/root.go:899

	if err != nil {
		return fmt.Errorf("failed to create stdout log file: %v", err)
	}
	defer stdout.Close()
	c.Stdout = stdout

	stderr, err := os.Create(stderrPath)
	if err != nil {
		return fmt.Errorf("failed to create stderr log file: %v", err)
	}
	defer stderr.Close()
	c.Stderr = stderr

	if err := c.Start(); err != nil {
		return fmt.Errorf("failed to start crush server: %v", err)
	}

	if err := c.Process.Release(); err != nil {
		return fmt.Errorf("failed to detach crush server process: %v", err)
	}

	return nil
}

func shouldEnableMetrics(cfg *config.Config) bool {
	if v, _ := strconv.ParseBool(os.Getenv("CRUSH_DISABLE_METRICS")); v {
		return false
	}
	if v, _ := strconv.ParseBool(os.Getenv("DO_NOT_TRACK")); v {
		return false
	}
	if cfg.Options.DisableMetrics {
		return false
	}
	return true
}

View on GitHub (pinned to 7944b8e522)