paperclipai/paperclip · error · RunnerGoalActionError

runner_goal_live_controller_unavailable

runner_goal_live_controller_unavailable

Error message

runner_goal_live_controller_unavailable: The live agent goal controller is unavailable; the run was not interrupted.

What it means

settleLiveRunnerGoalBeforeInterrupt throws RunnerGoalActionError(runner_goal_live_controller_unavailable) when it decided a pause/clear action is required, but neither the live adapter control dispatch nor the queued PRP command returned a completion promise. The stop command could not be handed to any controller, so the run was definitively NOT interrupted — the failure is reported instead of leaving an unknown state.

Source

Thrown at server/src/services/runner-goals.ts:983

      "The live agent goal cannot be stopped safely.",
    );
  }

  const requestId = `interrupt_${randomUUID()}`;
  const adapterControl = dispatchLiveRunnerGoalControl(binding, { requestId, action });
  const nativeControl = adapterControl
    ? null
    : queueLiveRunnerPrpCommand({
        ...binding,
        type: action === "pause" ? "session.goal.set" : "session.goal.clear",
        payload: action === "pause"
          ? { requestId, status: "paused" }
          : { requestId },
        commandId: `goal_${requestId}`,
      });
  const completion = adapterControl?.completion ?? nativeControl?.completion ?? null;
  if (!completion) {
    throw new RunnerGoalActionError(
      "runner_goal_live_controller_unavailable",
      "The live agent goal controller is unavailable; the run was not interrupted.",
    );
  }
  await completion;

  const deadline = Date.now() + 5_000;
  while (Date.now() < deadline) {
    const observed = await goals.projection(binding.companyId, binding.issueId, binding.agentId);
    if (action === "pause" && observed?.goal?.status === "paused") return "paused";
    if (action === "clear" && observed?.goal === null) return "cleared";
    await new Promise<void>((resolve) => setTimeout(resolve, 25));
  }
  throw new RunnerGoalActionError(
    "runner_goal_stop_unconfirmed",
    "The provider did not confirm that the live agent goal stopped.",
  );
}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Verify the agent adapter session is still alive and registered with dispatchLiveRunnerGoalControl before calling the interrupt path.
  2. Check logs for controller deregistration (process exit, reconnect) around the failure time and re-attach/restart the session.
  3. Ensure the PRP offline command queue (queueLiveRunnerPrpCommand) is accepting commands for this session; fix queue unavailability.
  4. Retry settleLiveRunnerGoalBeforeInterrupt once the session controller is re-established; the run was not interrupted so no cleanup is needed.

Example fix

// before: call stop immediately after process restart
await settleLiveRunnerGoalBeforeInterrupt(db, binding);
// after: ensure a controller exists first
const controller = getLiveController(binding);
if (!controller) await restartAgentSession(binding); // re-register live controller
await settleLiveRunnerGoalBeforeInterrupt(db, binding);
Defensive patterns

Strategy: fallback

When it happens

Trigger: dispatchLiveRunnerGoalControl(binding, {requestId, action}) returns null (no live adapter controller attached to the session) AND queueLiveRunnerPrpCommand(...) also returns null (offline/queued command path unavailable), so adapterControl?.completion ?? nativeControl?.completion is null.

Common situations: Interrupting a run after the adapter process/connection died so no live controller is registered; a service restart dropped the in-flight controller registration; the PRP command queue rejects or drops the command (e.g. session no longer tracked); calling the stop path for a session that was never bound to a live controller.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/901ed07686b4e2bc. Report an issue: GitHub.