{"record":{"id":"60158d472cd5ea99","repo":"vxcontrol/pentagi","slug":"failed-to-add-assistant-to-flow-w","errorCode":null,"errorMessage":"failed to add assistant to flow: %w","messagePattern":"failed to add assistant to flow: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/flows.go","lineNumber":324,"sourceCode":"\n\taw, err := NewAssistantWorker(ctx, newAssistantWorkerCtx{\n\t\tuserID:        userID,\n\t\tflowID:        flowID,\n\t\tinput:         input,\n\t\tprvname:       prvname,\n\t\tprvtype:       prvtype,\n\t\tuseAgents:     useAgents,\n\t\tfunctions:     functions,\n\t\tresources:     resources,\n\t\tfw:            fw,\n\t\tflowWorkerCtx: flowWorkerCtx,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create assistant: %w\", err)\n\t}\n\n\tif err = fw.AddAssistant(ctx, aw); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to add assistant to flow: %w\", err)\n\t}\n\n\treturn aw, nil\n}\n\nfunc (fc *flowController) ListFlows(ctx context.Context) []FlowWorker {\n\tfc.mx.Lock()\n\tdefer fc.mx.Unlock()\n\n\tflows := make([]FlowWorker, 0)\n\tfor _, flow := range fc.flows {\n\t\tflows = append(flows, flow)\n\t}\n\n\tsort.Slice(flows, func(i, j int) bool {\n\t\treturn flows[i].GetFlowID() < flows[j].GetFlowID()\n\t})\n","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/flows.go#L306-L342","documentation":"Wrap in CreateAssistant when fw.AddAssistant(ctx, aw) fails after the assistant worker was successfully created. AddAssistant registers the assistant with the flow worker (its internal tracking/subscriber state); failure means the assistant exists but is not attached to the flow, so the controller returns an error instead of a half-registered assistant.","triggerScenarios":"CreateAssistant succeeds through worker creation but fw.AddAssistant errors — e.g. the flow worker's internal state is being torn down concurrently, its assistant registry rejects the addition, or a context cancellation aborts the registration.","commonSituations":"Stopping the flow at the same moment an assistant is created (race between StopFlow and CreateAssistant), a flow worker that was closed after status checks, or context cancellation mid-registration.","solutions":["Retry CreateAssistant; transient races with flow shutdown resolve on a new attempt.","Ensure no concurrent StopFlow/FinishFlow is running for this flowID while adding assistants.","Check backend logs for the wrapped AddAssistant cause (subscriber/state errors).","If the flow is being torn down, create a new flow instead of reusing it."],"exampleFix":"// before\n// concurrent stop race\nwg.Add(2)\ngo ctl.StopFlow(ctx, flowID)\ngo ctl.CreateAssistant(ctx, userID, flowID, ...) // may fail here\n// after\n// serialize: add assistant first, then stop if needed\naw, err := ctl.CreateAssistant(ctx, userID, flowID, ...)\nif err == nil { go ctl.StopFlow(ctx, flowID) }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var aw AssistantWorker\nerr := retry(3, backoff, func() error {\n    var e error\n    aw, e = ctl.CreateAssistant(ctx, userID, flowID, ...)\n    if e != nil && strings.Contains(e.Error(), \"failed to add assistant to flow\") {\n        return e // transient race, retry\n    }\n    return nil //nolint: do not retry other causes\n})","preventionTips":["Don't call StopFlow/FinishFlow concurrently with CreateAssistant on the same flow.","Use one serialization point (per-flow lock) in client code for lifecycle + assistant ops.","Use sufficiently long-lived contexts so registration isn't canceled mid-way."],"tags":["go","concurrency","flow-worker","registration"],"backgroundTag":"assistant-registration-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}