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

  1. Inspect the wrapped inner error from taw.Finish to see whether it was DB failure or context cancellation.
  2. Retry AddAssistant after the failed Finish; the old worker remains registered, so the retry will attempt Finish again.
  3. Avoid passing a fresh context with a short timeout — give Finish a live ctx (context.WithoutCancel if in shutdown).
  4. Ensure only one worker per assistant ID is created (deduplicate at the task/subtask layer).
  5. 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

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


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/5914acbc1efdf51d. Report an issue: GitHub.