plandex-ai/plandex · error · shared.ApiError

{apiErr.Msg}

Error message

{apiErr.Msg}

What it means

This is the /health endpoint of the Plandex server surfacing a failure from the hooks.HealthCheck hook. When ExecHook returns a non-nil apiErr, the handler logs it and writes apiErr.Msg with apiErr.Status back to the client. The error means a registered health-check hook failed, so the server is reporting itself unhealthy.

Source

Thrown at app/server/routes/routes.go:37

func RegisterHandlePlandex(fn HandlePlandex) {
	HandlePlandexFn = fn
}

func EnsureHandlePlandex() {
	if HandlePlandexFn == nil {
		panic("handlePlandexFn is not set")
	}
}

func AddHealthRoutes(r *mux.Router) {
	EnsureHandlePlandex()

	HandlePlandexFn(r, "/health", false, func(w http.ResponseWriter, r *http.Request) {
		_, apiErr := hooks.ExecHook(hooks.HealthCheck, hooks.HookParams{})
		if apiErr != nil {
			log.Printf("Error in health check hook: %v\n", apiErr)
			http.Error(w, apiErr.Msg, apiErr.Status)
			return
		}
		fmt.Fprint(w, "OK")
	})

	HandlePlandexFn(r, "/version", false, func(w http.ResponseWriter, r *http.Request) {
		// Log the host
		host := r.Host
		log.Printf("Host header: %s", host)

		execPath, err := os.Executable()
		if err != nil {
			log.Fatal("Error getting current directory: ", err)
		}
		currentDir := filepath.Dir(execPath)

		// get version from version.txt
		var path string

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the server logs for 'Error in health check hook' to see the underlying hook error and its cause
  2. Verify the dependencies probed by the health-check hook (database, external services) are reachable and credentials are correct
  3. Retry /health once dependencies are restored; the message is per-request, not sticky
  4. If the hook itself is buggy, inspect hooks.ExecHook(hooks.HealthCheck) implementations and fix or unregister the failing hook

Example fix

// before
_, apiErr := hooks.ExecHook(hooks.HealthCheck, hooks.HookParams{})
if apiErr != nil {
	http.Error(w, apiErr.Msg, apiErr.Status)
	return
}
// after
_, apiErr := hooks.ExecHook(hooks.HealthCheck, hooks.HookParams{})
if apiErr != nil {
	log.Printf("health check failed: %v (msg=%s status=%d)", apiErr, apiErr.Msg, apiErr.Status)
	http.Error(w, apiErr.Msg, apiErr.Status)
	return
}
Defensive patterns

Strategy: try-catch

Try / catch

resp, err := http.Get(serverURL + "/health")
if err != nil {
	// server unreachable entirely
	return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
	body, _ := io.ReadAll(resp.Body)
	return fmt.Errorf("health check failed (status %d): %s", resp.StatusCode, string(body))
}

Prevention

When it happens

Trigger: Any registered hooks.HealthCheck hook returns an api.ApiError: typically a failed internal dependency check (e.g. database unreachable, migration failure, or another subsystem the hook probes) during GET /health.

Common situations: Load balancers or Kubernetes liveness/readiness probes hitting /health while the database is down or misconfigured; starting the server before its backing services are ready; broken hook registration after upgrades.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/a9999ec593e6d164. Report an issue: GitHub.