vxcontrol/pentagi · error
failed to finish assistant %d: %w
Error message
failed to finish assistant %d: %w
What it means
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.
Source
Thrown at backend/pkg/controller/flow.go:581
task, err := fw.tc.GetTask(ctx, taskID)
if err != nil {
return
}
task.InvalidateSubtasks(subtaskIDs)
}
func (fw *flowWorker) AddAssistant(ctx context.Context, aw AssistantWorker) error {
fw.awsMX.Lock()
defer fw.awsMX.Unlock()
if taw, ok := fw.aws[aw.GetAssistantID()]; ok {
if taw == aw {
return nil
}
if err := taw.Finish(ctx); err != nil {
return fmt.Errorf("failed to finish assistant %d: %w", aw.GetAssistantID(), err)
}
}
fw.aws[aw.GetAssistantID()] = aw
return nil
}
func (fw *flowWorker) GetAssistant(ctx context.Context, assistantID int64) (AssistantWorker, error) {
fw.awsMX.Lock()
defer fw.awsMX.Unlock()
if aw, ok := fw.aws[assistantID]; ok {
return aw, nil
}
return nil, fmt.Errorf("assistant %d not found", assistantID)
}View on GitHub (pinned to ea665308ba)
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).
Example fix
// before
ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond) // too short, old worker's Finish fails
if err := fw.AddAssistant(ctx, newAw); err != nil { return err }
// after
if err := fw.AddAssistant(context.WithoutCancel(ctx), newAw); err != nil {
log.WithError(err).Warn("old assistant finish failed; retrying registration")
return fw.AddAssistant(context.WithoutCancel(ctx), newAw)
} Defensive patterns
Strategy: retry
Try / catch
if err := fw.AddAssistant(ctx, aw); err != nil {
if strings.Contains(err.Error(), "failed to finish assistant") {
// old worker still registered; retry will re-attempt Finish
time.Sleep(200 * time.Millisecond)
return fw.AddAssistant(context.WithoutCancel(ctx), aw)
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- flow %d stopped: %w
- failed to create assistant: %w
- failed to add assistant to flow: %w
- subtask has already completed
- subtask is not waiting, run first
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/5914acbc1efdf51d.
Report an issue: GitHub.