{"record":{"id":"c0bb465a23740889","repo":"vxcontrol/pentagi","slug":"flow-not-found-c0bb46","errorCode":null,"errorMessage":"flow not found","messagePattern":"flow not found","errorType":"exception","errorClass":"ErrFlowNotFound","httpStatus":null,"severity":"warning","filePath":"backend/pkg/controller/flows.go","lineNumber":23,"sourceCode":"\t\"errors\"\n\t\"fmt\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n\n\t\"pentagi/pkg/config\"\n\t\"pentagi/pkg/database\"\n\t\"pentagi/pkg/docker\"\n\t\"pentagi/pkg/graph/subscriptions\"\n\t\"pentagi/pkg/providers\"\n\t\"pentagi/pkg/providers/provider\"\n\t\"pentagi/pkg/tools\"\n\n\t\"github.com/sirupsen/logrus\"\n)\n\nvar (\n\tErrFlowNotFound       = fmt.Errorf(\"flow not found\")\n\tErrFlowAlreadyStopped = fmt.Errorf(\"flow already stopped\")\n)\n\ntype FlowController interface {\n\tCreateFlow(\n\t\tctx context.Context,\n\t\tuserID int64,\n\t\tinput string,\n\t\tprvname provider.ProviderName,\n\t\tprvtype provider.ProviderType,\n\t\tfunctions *tools.Functions,\n\t\tresources []database.UserResource,\n\t) (FlowWorker, error)\n\tCreateAssistant(\n\t\tctx context.Context,\n\t\tuserID int64,\n\t\tflowID int64,\n\t\tinput string,","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/flows.go#L5-L41","documentation":"ErrFlowNotFound is the public sentinel error returned by GetFlow, StopFlow, FinishFlow, RenameFlow, DeleteFlow, and PatchAssistant when no flow with the given ID exists for the caller. It is a fixed message ('flow not found'), intended to be matched with errors.Is rather than string comparison. Callers should treat it as a 404-class condition.","triggerScenarios":"Any FlowController operation (GetFlow/StopFlow/FinishFlow/RenameFlow/DeleteFlow/PatchAssistant) called with a flowID that is not registered in the controller's flow map — e.g., after process restart before LoadFlows, or for an ID that never existed or was deleted.","commonSituations":"Client caches a flowID after server restart (workers not yet loaded); UI uses a stale ID after another user deleted the flow; race between DeleteFlow and a subsequent call; querying before startup LoadFlows completes.","solutions":["Verify the flowID via GetFlow or the flows list before mutating operations.","After a server restart, wait for LoadFlows to finish or re-fetch the flow list.","Handle it as 404 in the API layer and refresh client state.","Check errors.Is(err, controller.ErrFlowNotFound) instead of string matching."],"exampleFix":"// before\nflow, err := fc.GetFlow(ctx, flowID)\nif err != nil {\n\treturn fmt.Errorf(\"get flow: %w\", err)\n}\n// after\nflow, err := fc.GetFlow(ctx, flowID)\nif err != nil {\n\tif errors.Is(err, controller.ErrFlowNotFound) {\n\t\treturn http.StatusNotFound // or refresh flow list\n\t}\n\treturn fmt.Errorf(\"get flow: %w\", err)\n}","handlingStrategy":"type-guard","validationCode":"var exists bool\nerr := db.QueryRow(\"SELECT EXISTS(SELECT 1 FROM flows WHERE id=$1)\", flowID).Scan(&exists)\n// only call GetFlow/StopFlow/etc. when exists == true","typeGuard":"func IsFlowNotFound(err error) bool {\n\treturn errors.Is(err, controller.ErrFlowNotFound)\n}","tryCatchPattern":"flow, err := fc.GetFlow(ctx, flowID)\nif err != nil {\n\tif IsFlowNotFound(err) {\n\t\treturn ErrHTTP404 // refresh client flow list\n\t}\n\treturn fmt.Errorf(\"get flow: %w\", err)\n}","preventionTips":["Always match with errors.Is, never string comparison.","Re-fetch the flow list after server restarts (LoadFlows repopulates workers).","Treat as 404 in API handlers and update stale client IDs.","Guard against delete/use races by checking existence before mutations."],"tags":["not-found","sentinel-error","flow-lifecycle"],"backgroundTag":"resource-not-found","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}