vxcontrol/pentagi · info · ErrFlowAlreadyStopped
flow already stopped
Error message
flow already stopped
What it means
ErrFlowAlreadyStopped is the public sentinel error indicating the target flow is already in a stopped/finished state, so a stop operation is redundant. Returned by flow stop paths; match with errors.Is. It signals an idempotency conflict rather than a hard failure.
Source
Thrown at backend/pkg/controller/flows.go:24
"fmt"
"sort"
"sync"
"time"
"pentagi/pkg/config"
"pentagi/pkg/database"
"pentagi/pkg/docker"
"pentagi/pkg/graph/subscriptions"
"pentagi/pkg/providers"
"pentagi/pkg/providers/provider"
"pentagi/pkg/tools"
"github.com/sirupsen/logrus"
)
var (
ErrFlowNotFound = fmt.Errorf("flow not found")
ErrFlowAlreadyStopped = fmt.Errorf("flow already stopped")
)
type FlowController interface {
CreateFlow(
ctx context.Context,
userID int64,
input string,
prvname provider.ProviderName,
prvtype provider.ProviderType,
functions *tools.Functions,
resources []database.UserResource,
) (FlowWorker, error)
CreateAssistant(
ctx context.Context,
userID int64,
flowID int64,
input string,
useAgents bool,View on GitHub (pinned to ea665308ba)
Solutions
- Treat it as success/idempotent: the flow is stopped either way — check errors.Is(err, controller.ErrFlowAlreadyStopped) and continue.
- Disable the stop action in the UI once flow status is finished/stopped.
- Serialize stop operations or use per-flow locking if concurrent stops are common.
- Verify current flow status via GetFlow before stopping.
Example fix
// before
if err := fc.StopFlow(ctx, flowID); err != nil {
return fmt.Errorf("stop flow: %w", err)
}
// after
if err := fc.StopFlow(ctx, flowID); err != nil && !errors.Is(err, controller.ErrFlowAlreadyStopped) {
return fmt.Errorf("stop flow: %w", err)
} Defensive patterns
Strategy: type-guard
Validate before calling
var status string
err := db.QueryRow("SELECT status FROM flows WHERE id=$1", flowID).Scan(&status)
// skip StopFlow if status is already finished/stopped Type guard
func IsFlowAlreadyStopped(err error) bool {
return errors.Is(err, controller.ErrFlowAlreadyStopped)
} Try / catch
err := fc.StopFlow(ctx, flowID)
if err != nil && !IsFlowAlreadyStopped(err) {
return fmt.Errorf("stop flow: %w", err)
}
// ErrFlowAlreadyStopped is treated as idempotent success Prevention
- Treat this error as success — the desired end state is already reached.
- Disable stop buttons once flow status shows stopped/finished.
- Deduplicate stop requests (per-flow mutex or request ID).
- Check flow status via GetFlow before issuing a stop.
When it happens
Trigger: Calling StopFlow (or related termination APIs) on a flow that was already stopped/finished — e.g., double-clicking a stop button, retries after a timeout, or concurrent stop requests.
Common situations: Duplicate stop requests from the UI; retry logic re-issuing a stop that actually succeeded; two operators stopping the same flow concurrently.
Related errors
- flow not found
- flow %d has status %s: loading aborted: %w
- failed to load flows: %w
- flow %d is not completed
- failed to stop flow %d: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/dad2d5d7a15ffe57.
Report an issue: GitHub.