vxcontrol/pentagi · warning

stop timed out after %s — the task may still be winding down

Error message

stop timed out after %s — the task may still be winding down. Call %s to check the current state before proceeding

What it means

The stop handler was given a bounded context (flowOperationTimeout) and returned context.DeadlineExceeded or context.Canceled, so stop_flow could not confirm the flow stopped within the operation window. The message is deliberately advisory: the stop may still complete asynchronously, and the caller is told to re-check with get_flow_status.

Source

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

	isRunning := false
	for _, task := range tasks {
		if task.Status == database.TaskStatusRunning {
			isRunning = true
			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 {

View on GitHub (pinned to ea665308ba)

Solutions

  1. Call get_flow_status to check whether the flow actually stopped; it may have finished after the timeout.
  2. Wait a few seconds and call stop_flow again if it is still running.
  3. Check for long-running containers/commands in the flow's sandbox and terminate them if the flow refuses to wind down.
  4. Inspect backend logs to see whether the stop handler completed after the deadline.

Example fix

// after a stop timeout, verify before retrying
const status = await getFlowStatus(flowId);
if (status === 'running') {
  await stopFlow(flowId, 'retry stop');
}
Defensive patterns

Strategy: fallback

Try / catch

try {
  await tool.call('stop_flow', { reason: 'stop requested' });
} catch (err) {
  if (String(err).includes('stop timed out')) {
    // stop may complete async: verify, then retry if still running
    const status = await tool.call('get_flow_status', { detail: 'running' });
    if (stillRunning(status)) await tool.call('stop_flow', { reason: 'retry stop' });
  }
}

Prevention

When it happens

Trigger: Calling stop_flow when the underlying flow shutdown takes longer than flowOperationTimeout (graceful in-flight tool execution finishing), or the handler's context is cancelled externally during the stop.

Common situations: Stopping a flow whose executor is mid-command in a Docker sandbox; long-running terminal commands that ignore cancellation; heavily loaded backend slowing the shutdown path.

Understand the failure class

Related errors


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