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

  1. Verify the flow is active before requesting its terminal-log worker
  2. Map this error to 404 in GraphQL/REST handlers and prompt a flow refresh on the client
  3. Re-fetch the flow list after backend restarts instead of caching flow IDs
  4. In multi-instance deployments, pin flow requests to the owning instance or persist worker registry state
  5. 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

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.