paperclipai/paperclip · warning · RunnerGoalActionError

goal_token_budget_unsupported

goal_token_budget_unsupported

Error message

goal_token_budget_unsupported: This agent session does not support goal token budgets.

What it means

`act` rejects goal actions carrying a `tokenBudget` when the session's capability lacks `tokenBudgetControl`, throwing code `goal_token_budget_unsupported`. Goal-level token budgets are an optional adapter capability; requesting one on a session that can't enforce it is a hard error.

Source

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

      if (session.goalRevision !== request.expectedRevision) {
        throw new RunnerGoalConflictError("stale_revision", currentProjection);
      }
      const capability = storedCapability(session, fallbackCapability);
      const requiredCapabilityAction = request.action === "pause"
        ? "pause"
        : request.action === "resume"
          ? "resume"
          : request.action === "clear"
            ? "clear"
            : "set";
      if (capability.availability !== "available" || !capability.actions.includes(requiredCapabilityAction)) {
        throw new RunnerGoalActionError(
          capability.reasonCode ?? "goal_action_unsupported",
          capability.reason ?? `${request.action} is unsupported by this agent session.`,
        );
      }
      if (request.tokenBudget != null && !capability.tokenBudgetControl) {
        throw new RunnerGoalActionError(
          "goal_token_budget_unsupported",
          "This agent session does not support goal token budgets.",
        );
      }
      if (request.action === "create" && currentGoal && currentGoal.status !== "complete") {
        throw new RunnerGoalConflictError("replacement_required", currentProjection);
      }
      if (["edit", "replace", "pause", "resume", "clear"].includes(request.action) && !currentGoal) {
        throw new RunnerGoalActionError("goal_not_found", "There is no current session goal.");
      }

      const nextRevision = session.goalRevision + 1;
      const desiredState = request.action === "pause"
        ? "paused"
        : request.action === "clear"
          ? null
          : "active";
      await tx.update(agentTaskSessions).set({

View on GitHub (pinned to 01ad858492)

Solutions

  1. Omit `tokenBudget` from the request when `capability.tokenBudgetControl` is falsy
  2. Check the projection before sending: `if (proj.capability.tokenBudgetControl)` gate the field
  3. Enforce budgets out-of-band (board-level budget auto-pause) for adapters without goal budget support
  4. Upgrade to an adapter that supports token budget control if budgets are required

Example fix

// before
await act(companyId, issueId, { agentId, action: 'set', goal, tokenBudget: 5000 });
// after
const proj = await projection(companyId, issueId, agentId);
await act(companyId, issueId, { agentId, action: 'set', goal,
  ...(proj.capability.tokenBudgetControl ? { tokenBudget: 5000 } : {}) });
Defensive patterns

Strategy: validation

Validate before calling

const proj = await runnerGoals.projection(companyId, issueId, agentId);
if (tokenBudget != null && !proj.capability.tokenBudgetControl) {
  delete req.tokenBudget; // or reject at the call site
}

Type guard

function supportsTokenBudget(proj) {
  return proj?.capability?.tokenBudgetControl === true;
}

Try / catch

try {
  await runnerGoals.act(companyId, issueId, req);
} catch (e) {
  if (e instanceof RunnerGoalActionError && e.code === 'goal_token_budget_unsupported') {
    const { tokenBudget, ...rest } = req;
    return await runnerGoals.act(companyId, issueId, rest); // retry without budget
  }
  throw e;
}

Prevention

When it happens

Trigger: Including `tokenBudget` in RunnerGoalActionRequest for an agent/adapter that does not advertise `capability.tokenBudgetControl`; blindly forwarding a tokenBudget field from generic goal tooling.

Common situations: Generic goal-management UI always sends tokenBudget; migrating workflows between adapters with differing budget support; adapter capability probe didn't detect budget control so the field is unsupported.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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