vxcontrol/pentagi · error

assistant %d not found

Error message

assistant %d not found

What it means

flowWorker.GetAssistant looks up a registered AssistantWorker by ID under the aws mutex; if the map has no entry it returns 'assistant %d not found'. The registry holds only assistants spawned and still alive on this flow worker — deleted or never-started assistants are absent.

Source

Thrown at backend/pkg/controller/flow.go:598

		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)
}

func (fw *flowWorker) DeleteAssistant(ctx context.Context, assistantID int64) error {
	fw.awsMX.Lock()
	defer fw.awsMX.Unlock()

	aw, ok := fw.aws[assistantID]
	if ok {
		if err := aw.Finish(ctx); err != nil {
			return fmt.Errorf("failed to finish assistant %d: %w", assistantID, err)
		}

		delete(fw.aws, assistantID)
	}

	if assistant, err := fw.flowCtx.DB.DeleteAssistant(ctx, assistantID); err != nil {
		return fmt.Errorf("failed to delete assistant %d: %w", assistantID, err)
	} else {

View on GitHub (pinned to ea665308ba)

Solutions

  1. Call ListAssistants first and match the ID against live workers before fetching.
  2. Re-create/register the assistant via AddAssistant if it should exist but was dropped (e.g. after reload).
  3. Confirm the assistant belongs to the same flow — IDs are only valid within one flowWorker.
  4. Treat as not-found (404) in API layers and refresh the client's assistant list.
  5. Serialize create-then-get sequences to avoid the registration race.

Example fix

// before
aw, err := fw.GetAssistant(ctx, assistantID) // fails after reload: worker not in map
// after
for _, live := range fw.ListAssistants(ctx) {
    if live.GetAssistantID() == assistantID {
        return use(live)
    }
}
return fmt.Errorf("assistant %d is not active on this flow", assistantID)
Defensive patterns

Strategy: validation

Validate before calling

live := fw.ListAssistants(ctx)
found := false
for _, a := range live {
    if a.GetAssistantID() == assistantID { found = true; break }
}
if !found { return ErrAssistantNotActive }

Type guard

func assistantActive(fw FlowWorker, ctx context.Context, id int64) bool {
    for _, a := range fw.ListAssistants(ctx) {
        if a.GetAssistantID() == id { return true }
    }
    return false
}

Try / catch

aw, err := fw.GetAssistant(ctx, assistantID)
if err != nil {
    if strings.Contains(err.Error(), "not found") { return http.StatusNotFound }
    return http.StatusInternalServerError
}

Prevention

When it happens

Trigger: Calling GetAssistant with an ID for an assistant that was never added on this flow, was removed via DeleteAssistant, was registered on a different flow's worker, or with a stale ID after flow reload (in-memory map repopulated only for active assistants).

Common situations: Client sends an assistant ID from a previous session after server restart; cross-flow ID reuse; DeleteAssistant already removed the worker but the caller caches the ID; race where the caller queries before AddAssistant completes.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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