plandex-ai/plandex · info

shared.NoBuildsErr

Error message

shared.NoBuildsErr

What it means

When modelPlan.Build succeeds but reports numBuilds == 0, there was nothing pending to build (no queued file changes from a prior tell). The handler responds 404 with shared.NoBuildsErr (the client SDK recognizes this sentinel string) and logs an info-level notification. It is an expected 'nothing to do' outcome, not a fault.

Source

Thrown at app/server/handlers/plans_exec.go:205

		Plan:          plan,
		Branch:        branch,
		Auth:          auth,
		SessionId:     requestBody.SessionId,
		OrgUserConfig: orgUserConfig,
		Settings:      settings,
	})

	if err != nil {
		log.Printf("Error building plan: %v\n", err)
		go notify.NotifyErr(notify.SeverityError, fmt.Errorf("error building plan: %v", err))
		http.Error(w, "Error building plan", http.StatusInternalServerError)
		return
	}

	if numBuilds == 0 {
		log.Println("No builds were executed")
		go notify.NotifyErr(notify.SeverityInfo, fmt.Errorf("no builds were executed"))
		http.Error(w, shared.NoBuildsErr, http.StatusNotFound)
		return
	}

	if requestBody.ConnectStream {
		startResponseStream(r.Context(), w, auth, planId, branch, false)
	}

	log.Println("Successfully processed request for BuildPlanHandler")
}

func ConnectPlanHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Received request for ConnectPlanHandler", "ip:", host.Ip)

	vars := mux.Vars(r)
	planId := vars["planId"]
	branch := vars["branch"]
	log.Println("planId: ", planId)
	log.Println("branch: ", branch)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Treat the NoBuildsErr 404 as a benign no-op — check the response body for the sentinel string and continue.
  2. Run `plandex tell` first if you expect changes; build only processes output from a tell.
  3. De-duplicate automation so build is invoked once per tell, ideally driven by the stream completion event.
  4. Poll build status (GetBuildStatus) instead of re-invoking build when unsure whether builds are pending.

Example fix

// before: blindly retrying the 404
if err != nil { return retry() }
// after: recognize the no-builds sentinel
if res.StatusCode == 404 && strings.Contains(res.Body, shared.NoBuildsErr) {
    return nil // nothing pending; not an error
}
Defensive patterns

Strategy: type-guard

Validate before calling

// check for pending builds via status endpoint instead of invoking build blindly
res, _ := http.Get(baseURL + "/plans/" + planId + "/branch/" + branch + "/build_status")

Type guard

func isNoBuildsErr(statusCode int, body string) bool {
    return statusCode == http.StatusNotFound && strings.Contains(body, shared.NoBuildsErr)
}

Try / catch

if res.StatusCode == 404 && isNoBuildsErr(res.StatusCode, res.Body) {
    return nil // benign: nothing to build
}
if res.StatusCode != 200 { return fmt.Errorf("build failed: %s", res.Body) }

Prevention

When it happens

Trigger: Calling the build endpoint when no tell has queued any changes, after builds already ran to completion (double-build), or after the previous build consumed all pending updates.

Common situations: CI scripts invoking build twice; client retrying a build after a successful stream ended; automation assuming build is idempotent; race where another client built first.

Related errors


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