vxcontrol/pentagi · error
flow not found
Error message
flow not found
What it means
GetFlowTermLog looks up the per-flow FlowTermLogWorker in an in-memory map under a mutex. This error means no terminal-log worker is registered for the given flow ID — the flow was never created, was already removed upon completion, or the process restarted and lost the in-memory registry. It is a lookup miss, not a database error.
Source
Thrown at backend/pkg/controller/termlogs.go:65
func (tlc *termLogController) ListFlowsTermLog(ctx context.Context) ([]FlowTermLogWorker, error) {
tlc.mx.Lock()
defer tlc.mx.Unlock()
flows := make([]FlowTermLogWorker, 0, len(tlc.flows))
for _, flw := range tlc.flows {
flows = append(flows, flw)
}
return flows, nil
}
func (tlc *termLogController) GetFlowTermLog(ctx context.Context, flowID int64) (FlowTermLogWorker, error) {
tlc.mx.Lock()
defer tlc.mx.Unlock()
flw, ok := tlc.flows[flowID]
if !ok {
return nil, fmt.Errorf("flow not found")
}
return flw, nil
}
func (tlc *termLogController) GetFlowContainers(ctx context.Context, flowID int64) ([]database.Container, error) {
tlc.mx.Lock()
defer tlc.mx.Unlock()
flw, ok := tlc.flows[flowID]
if !ok {
return nil, fmt.Errorf("flow not found")
}
return flw.GetContainers(ctx)
}
View on GitHub (pinned to ea665308ba)
Solutions
- Verify the flow is active before requesting its terminal-log worker
- Map this error to 404 in GraphQL/REST handlers and prompt a flow refresh on the client
- Re-fetch the flow list after backend restarts instead of caching flow IDs
- In multi-instance deployments, pin flow requests to the owning instance or persist worker registry state
- If a worker is legitimately needed, register it through the termLogController's creation path
Example fix
// before
worker, err := tlc.GetFlowTermLog(ctx, flowID)
if err != nil { return err } // 500 for a normal lookup miss
// after
worker, err := tlc.GetFlowTermLog(ctx, flowID)
if err != nil && strings.Contains(err.Error(), "flow not found") {
return nil, statusError(http.StatusNotFound, "flow not found")
} Defensive patterns
Strategy: type-guard
Validate before calling
func flowKnown(flowID int64, knownFlows map[int64]struct{}) bool {
_, ok := knownFlows[flowID]
return ok
} Type guard
func isFlowNotFound(err error) bool {
return err != nil && strings.Contains(err.Error(), "flow not found")
} Try / catch
worker, err := tlc.GetFlowTermLog(ctx, flowID)
if isFlowNotFound(err) {
return nil, httpErr(http.StatusNotFound, "terminal log for flow %d not found", flowID)
}
if err != nil {
return nil, err
} Prevention
- Confirm the flow is active before subscribing to its terminal log
- Handle worker eviction on flow completion — expect this error for finished flows
- Refresh flow IDs client-side after backend restarts or redeploys
- Map the lookup miss to 404 rather than retrying
- In multi-instance deployments, ensure sticky routing to the flow-owning instance
When it happens
Trigger: Querying terminal logs for a flowID before flow creation completes, after the flow finished and its worker was evicted, or after a backend restart with stale client IDs.
Common situations: Users reopening a finished flow's terminal view after redeploy; load balancers routing to an instance that doesn't own the flow; race between CreateFlow and the first log subscription.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/3cb524d0755048fe.
Report an issue: GitHub.