{"record":{"id":"3cb524d0755048fe","repo":"vxcontrol/pentagi","slug":"flow-not-found-3cb524","errorCode":null,"errorMessage":"flow not found","messagePattern":"flow not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/termlogs.go","lineNumber":65,"sourceCode":"func (tlc *termLogController) ListFlowsTermLog(ctx context.Context) ([]FlowTermLogWorker, error) {\n\ttlc.mx.Lock()\n\tdefer tlc.mx.Unlock()\n\n\tflows := make([]FlowTermLogWorker, 0, len(tlc.flows))\n\tfor _, flw := range tlc.flows {\n\t\tflows = append(flows, flw)\n\t}\n\n\treturn flows, nil\n}\n\nfunc (tlc *termLogController) GetFlowTermLog(ctx context.Context, flowID int64) (FlowTermLogWorker, error) {\n\ttlc.mx.Lock()\n\tdefer tlc.mx.Unlock()\n\n\tflw, ok := tlc.flows[flowID]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"flow not found\")\n\t}\n\n\treturn flw, nil\n}\n\nfunc (tlc *termLogController) GetFlowContainers(ctx context.Context, flowID int64) ([]database.Container, error) {\n\ttlc.mx.Lock()\n\tdefer tlc.mx.Unlock()\n\n\tflw, ok := tlc.flows[flowID]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"flow not found\")\n\t}\n\n\treturn flw.GetContainers(ctx)\n}\n","sourceCodeStart":47,"sourceCodeEnd":82,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/termlogs.go#L47-L82","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nworker, err := tlc.GetFlowTermLog(ctx, flowID)\nif err != nil { return err } // 500 for a normal lookup miss\n// after\nworker, err := tlc.GetFlowTermLog(ctx, flowID)\nif err != nil && strings.Contains(err.Error(), \"flow not found\") {\n    return nil, statusError(http.StatusNotFound, \"flow not found\")\n}","handlingStrategy":"type-guard","validationCode":"func flowKnown(flowID int64, knownFlows map[int64]struct{}) bool {\n    _, ok := knownFlows[flowID]\n    return ok\n}","typeGuard":"func isFlowNotFound(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"flow not found\")\n}","tryCatchPattern":"worker, err := tlc.GetFlowTermLog(ctx, flowID)\nif isFlowNotFound(err) {\n    return nil, httpErr(http.StatusNotFound, \"terminal log for flow %d not found\", flowID)\n}\nif err != nil {\n    return nil, err\n}","preventionTips":["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"],"tags":["not-found","flow","lookup"],"backgroundTag":"resource-not-found","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}