plandex-ai/plandex · warning

no active plan with id %s

Error message

no active plan with id %s

What it means

Stop attempts to cancel a currently active plan by looking it up in the in-memory registry. If no plan with that ID + branch is active, it returns 'no active plan with id %s'. The plan may have already finished, been stopped, or never started on this server.

Source

Thrown at app/server/model/plan/stop.go:14

package plan

import (
	"fmt"
	"plandex-server/db"

	"github.com/sashabaranov/go-openai"
)

func Stop(planId, branch, currentUserId, currentOrgId string) error {
	active := GetActivePlan(planId, branch)

	if active == nil {
		return fmt.Errorf("no active plan with id %s", planId)
	}

	active.SummaryCancelFn()
	active.CancelFn()

	return nil
}

func StorePartialReply(repo *db.GitRepo, planId, branch, currentUserId, currentOrgId string) error {
	active := GetActivePlan(planId, branch)

	if active == nil {
		return fmt.Errorf("no active plan with id %s", planId)
	}

	if !active.BuildOnly && !active.RepliesFinished {
		num := active.MessageNum + 1

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the plan's status first (GET plan) — if it already finished/errored, no stop is needed
  2. Verify planId and branch exactly match the running plan
  3. Treat this error as idempotent no-op in clients: if the plan is not active, stop succeeded trivially
  4. In multi-replica deployments, route stop requests to the same instance holding the active plan (sticky sessions)

Example fix

// before
if err := plan.Stop(planId, branch, userId, orgId); err != nil {
    return err
}
// after: tolerate already-stopped plans
if err := plan.Stop(planId, branch, userId, orgId); err != nil && !strings.Contains(err.Error(), "no active plan") {
    return err
}
Defensive patterns

Strategy: type-guard

Validate before calling

// check the plan status before attempting to stop
status := getPlanStatus(planId, branch)
if status != "active" && status != "streaming" {
    return nil // already finished; stop is a no-op
}

Type guard

func isActive(p *types.ActivePlan) bool { return p != nil }

Try / catch

if err := plan.Stop(planId, branch, userId, orgId); err != nil {
    if strings.Contains(err.Error(), "no active plan with id") {
        return nil // idempotent stop: plan already finished
    }
    return err
}

Prevention

When it happens

Trigger: Calling Stop (POST /plans/{id}/{branch}/stop) after the plan already completed; double-stop race where a first Stop already canceled and deregistered the plan; planId/branch mismatch (wrong branch name); server restarted so in-memory active plans were lost; calling Stop on a plan running on a different server instance.

Common situations: UI showing a plan as running due to stale state while it already finished; load-balanced deployments without sticky sessions hitting a replica that doesn't hold the active plan; typos in branch names in API calls.

Related errors


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