vxcontrol/pentagi · error

failed to stop flow: %w

Error message

failed to stop flow: %w

What it means

stop_flow invoked the flow-stop handler and it returned an error that is neither DeadlineExceeded nor Canceled. The raw error is wrapped, preserving the underlying reason (handler failure, internal error, store failure). The flow may or may not have been stopped.

Source

Thrown at backend/pkg/tools/flow_manager.go:650

			break
		}
	}

	if !isRunning {
		return "No running task found — the flow is already in 'waiting' state and ready to accept input.", nil
	}

	stopCtx, stopCancel := context.WithTimeout(ctx, flowOperationTimeout)
	defer stopCancel()

	if err := t.handler(stopCtx, action.Reason); err != nil {
		if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
			return "", fmt.Errorf(
				"stop timed out after %s — the task may still be winding down. "+
					"Call %s to check the current state before proceeding",
				flowOperationTimeout, GetFlowStatusToolName)
		}
		return "", fmt.Errorf("failed to stop flow: %w", err)
	}

	// Re-query task statuses to report the actual state the flow reached.
	newTasks, err := t.db.GetFlowTasks(ctx, t.flowID)
	if err != nil {
		// Stop succeeded but we cannot verify the new state — return a safe partial message.
		return fmt.Sprintf(
			"Flow stop initiated (reason: %s). Could not verify new status: %s. "+
				"Call %s to confirm before proceeding.",
			action.Reason, err, GetFlowStatusToolName), nil
	}

	for _, task := range newTasks {
		if task.Status == database.TaskStatusRunning {
			return fmt.Sprintf(
				"Stop requested (reason: %s), but a task is still running — the flow has not reached 'waiting' yet. "+
					"Call %s to confirm before making further changes.",
				action.Reason, GetFlowStatusToolName), nil

View on GitHub (pinned to ea665308ba)

Solutions

  1. Read the wrapped cause (%v / errors.Is) to identify the handler failure.
  2. Call get_flow_status to determine the flow's actual state before attempting anything else.
  3. Retry stop_flow once transient conditions clear.
  4. If persistent, capture backend logs and restart the pentagi service; report if reproducible.

Example fix

// verify actual state after failed stop
status, _ := getFlowStatus(flowId)
if status == 'running' {
    // retry stop_flow
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await tool.call('stop_flow', { reason: 'stop requested' });
} catch (err) {
  if (String(err).includes('failed to stop flow') && !String(err).includes('timed out')) {
    log.error('stop handler failed', parseWrappedCause(err));
    // verify state and retry
    const status = await tool.call('get_flow_status', { detail: 'summary' });
  }
}

Prevention

When it happens

Trigger: Calling stop_flow when the injected stop handler fails for a non-context reason — e.g. an internal error while signalling cancellation to the flow's goroutines, or a store error during the stop path.

Common situations: Backend-internal failure while propagating cancellation; race with flow lifecycle transitions; bugs or resource exhaustion (goroutines, DB pool) inside the stop handler.

Related errors


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