plandex-ai/plandex · error

active plan not found for plan ID %s on branch %s

Error message

active plan not found for plan ID %s on branch %s

What it means

onActivePlanMissingError in tell_stream_error.go centralizes handling for a missing in-memory active plan during an active tell stream. When any stream handler (processChunk, handleMissingFile, handleDescAndExecStatus, handleStreamFinished) finds GetActivePlan(planId, branch) nil, this method logs and routes the formatted 'active plan not found for plan ID %s on branch %s' error through state.onError with storeDesc=true so the description is persisted.

Source

Thrown at app/server/model/plan/tell_stream_error.go:267

		active.StreamDoneCh <- &shared.ApiError{
			Type:   shared.ApiErrorTypeOther,
			Status: http.StatusInternalServerError,
			Msg:    msg,
		}
	}

	return onErrorResult{
		shouldContinueMainLoop: true,
	}
}

func (state *activeTellStreamState) onActivePlanMissingError() {
	planId := state.plan.Id
	branch := state.branch
	log.Printf("Active plan not found for plan ID %s on branch %s\n", planId, branch)
	state.onError(onErrorParams{
		streamErr: fmt.Errorf("active plan not found for plan ID %s on branch %s", planId, branch),
		storeDesc: true,
	})
}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check server logs for which handler triggered it and whether a prior finish already removed the plan
  2. Ensure only one code path clears the active plan (guard with sync.Once or state flag)
  3. Compare branch strings at stream start vs chunk processing
  4. If caused by a stale client stream, close the connection instead of retrying

Example fix

// before
func (state *activeTellStreamState) onActivePlanMissingError() {
    state.onError(onErrorParams{
        streamErr: fmt.Errorf("active plan not found for plan ID %s on branch %s", planId, branch),
        storeDesc: true,
    })
}
// after
func (state *activeTellStreamState) onActivePlanMissingError() {
    if state.cleanedUp.Load() { // already finished; don't double-handle
        return
    }
    state.onError(onErrorParams{
        streamErr: fmt.Errorf("active plan not found for plan ID %s on branch %s", planId, branch),
        storeDesc: true,
    })
}
Defensive patterns

Strategy: validation

Validate before calling

if GetActivePlan(state.plan.Id, state.branch) == nil {
    // skip chunk processing entirely; stream is already torn down
    return
}

Type guard

func (state *activeTellStreamState) hasActivePlan() bool {
    return GetActivePlan(state.plan.Id, state.branch) != nil
}

Try / catch

if GetActivePlan(planId, branch) == nil {
    state.onActivePlanMissingError()
    return
}

Prevention

When it happens

Trigger: Active plan evicted/removed mid-stream (stream finished on another path), branch identifier mismatch between stream start and chunk processing, concurrent stream-finish paths racing to clear the active plan, or server state loss.

Common situations: Double handling of a finished stream; client retries chunks after the active plan was cleaned up; branch name casing/prefix differs between registration and lookup.

Related errors


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