{"record":{"id":"5914acbc1efdf51d","repo":"vxcontrol/pentagi","slug":"failed-to-finish-assistant-d-w","errorCode":null,"errorMessage":"failed to finish assistant %d: %w","messagePattern":"failed to finish assistant (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/flow.go","lineNumber":581,"sourceCode":"\ttask, err := fw.tc.GetTask(ctx, taskID)\n\tif err != nil {\n\t\treturn\n\t}\n\n\ttask.InvalidateSubtasks(subtaskIDs)\n}\n\nfunc (fw *flowWorker) AddAssistant(ctx context.Context, aw AssistantWorker) error {\n\tfw.awsMX.Lock()\n\tdefer fw.awsMX.Unlock()\n\n\tif taw, ok := fw.aws[aw.GetAssistantID()]; ok {\n\t\tif taw == aw {\n\t\t\treturn nil\n\t\t}\n\n\t\tif err := taw.Finish(ctx); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to finish assistant %d: %w\", aw.GetAssistantID(), err)\n\t\t}\n\t}\n\n\tfw.aws[aw.GetAssistantID()] = aw\n\n\treturn nil\n}\n\nfunc (fw *flowWorker) GetAssistant(ctx context.Context, assistantID int64) (AssistantWorker, error) {\n\tfw.awsMX.Lock()\n\tdefer fw.awsMX.Unlock()\n\n\tif aw, ok := fw.aws[assistantID]; ok {\n\t\treturn aw, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"assistant %d not found\", assistantID)\n}","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/flow.go#L563-L599","documentation":"AddAssistant registers an AssistantWorker on the flow, keyed by assistant ID. If a different worker is already registered for that ID, the existing one is finished first; if taw.Finish(ctx) fails, AddAssistant wraps and returns 'failed to finish assistant %d' and the new worker is NOT stored. The inner error comes from the previous assistant's Finish implementation.","triggerScenarios":"Re-adding an assistant with an ID already owned by a different AssistantWorker instance (e.g. re-created subtask worker) while Finish on the old worker fails — its internal DB write, provider teardown, or cancelled ctx bubbles up.","commonSituations":"Resuming/retrying a subtask creates a second worker for the same assistant ID; server restart paths recreating workers while old ones hold resources; context cancellation during Finish making its DB update fail.","solutions":["Inspect the wrapped inner error from taw.Finish to see whether it was DB failure or context cancellation.","Retry AddAssistant after the failed Finish; the old worker remains registered, so the retry will attempt Finish again.","Avoid passing a fresh context with a short timeout — give Finish a live ctx (context.WithoutCancel if in shutdown).","Ensure only one worker per assistant ID is created (deduplicate at the task/subtask layer).","Check the old worker's Finish path for resource leaks (unclosed Docker sessions, pending DB writes)."],"exampleFix":"// before\nctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond) // too short, old worker's Finish fails\nif err := fw.AddAssistant(ctx, newAw); err != nil { return err }\n// after\nif err := fw.AddAssistant(context.WithoutCancel(ctx), newAw); err != nil {\n    log.WithError(err).Warn(\"old assistant finish failed; retrying registration\")\n    return fw.AddAssistant(context.WithoutCancel(ctx), newAw)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := fw.AddAssistant(ctx, aw); err != nil {\n    if strings.Contains(err.Error(), \"failed to finish assistant\") {\n        // old worker still registered; retry will re-attempt Finish\n        time.Sleep(200 * time.Millisecond)\n        return fw.AddAssistant(context.WithoutCancel(ctx), aw)\n    }\n    return err\n}","preventionTips":["Deduplicate worker creation per assistant ID at the task layer","Give Finish a live context with adequate timeout (not a tiny WithTimeout)","Log the wrapped inner error to identify which Finish subsystem failed","Avoid re-registering assistants during shutdown without WithoutCancel"],"tags":["go","assistant-worker","lifecycle","concurrency"],"backgroundTag":"assistant-finish-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}