paperclipai/paperclip · error · RunnerGoalActionError

agent_not_assigned

agent_not_assigned

Error message

agent_not_assigned: The selected agent is not assigned to this issue.

What it means

The runner goal action API (`act` in server/src/services/runner-goals.ts) validates that the agentId in the request is the current assignee of the issue. It throws RunnerGoalActionError with code `agent_not_assigned` when `binding.issue.assigneeAgentId !== request.agentId`. This enforces the single-assignee task model: only the assigned agent may manage session goals.

Source

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

      goal,
      workingNow: goal?.workingNow ?? false,
      activeRunId: activeRun?.id ?? null,
      pendingAction: projectedPendingAction,
      revision: session?.goalRevision ?? 0,
      observedAt: session?.goalObservedAt?.toISOString() ?? null,
    };
  }

  async function act(
    companyId: string,
    issueId: string,
    request: RunnerGoalActionRequest,
  ): Promise<RunnerGoalActionAccepted> {
    const binding = await readBinding(companyId, issueId, request.agentId);
    if (!binding) throw new RunnerGoalActionError("issue_not_found", "Issue not found.");
    if (!binding.agent) throw new RunnerGoalActionError("agent_not_found", "Agent not found.");
    if (binding.issue.assigneeAgentId !== request.agentId) {
      throw new RunnerGoalActionError("agent_not_assigned", "The selected agent is not assigned to this issue.");
    }
    const fallbackCapability = capabilityForAgent(binding.agent);
    const initialProjection = await projection(companyId, issueId, request.agentId);
    if (!initialProjection) throw new RunnerGoalActionError("issue_not_found", "Issue not found.");
    if (initialProjection.capability.availability !== "available") {
      throw new RunnerGoalActionError(
        initialProjection.capability.reasonCode ?? "session_goals_unsupported",
        initialProjection.capability.reason ?? "Session goals are unsupported.",
      );
    }

    const accepted = await db.transaction(async (tx) => {
      await tx.insert(agentTaskSessions).values({
        companyId,
        agentId: request.agentId,
        adapterType: binding.agent!.adapterType,
        taskKey: issueId,
      }).onConflictDoNothing();

View on GitHub (pinned to 01ad858492)

Solutions

  1. Fetch the issue and check `assigneeAgentId` before calling, and use that agent's id in the request
  2. Re-read the issue (assignment may have changed) and retry with the current assignee
  3. If your agent should own the issue, reassign it first through the issue assignment API
  4. Check company scoping: ensure you are querying the right company/issue so the binding matches your agent

Example fix

// before
await runnerGoals.act(companyId, issueId, { agentId: someAgentId, action: 'pause', ... });
// after
const issue = await getIssue(companyId, issueId);
if (!issue || issue.assigneeAgentId !== someAgentId) return; // or use issue.assigneeAgentId
await runnerGoals.act(companyId, issueId, { agentId: issue.assigneeAgentId, action: 'pause', ... });
Defensive patterns

Strategy: validation

Validate before calling

const issue = await getIssue(companyId, issueId);
if (!issue || issue.assigneeAgentId !== agentId) {
  throw new Error(`agent ${agentId} is not the assignee of issue ${issueId}`);
}

Type guard

function isAssigned(issue, agentId) {
  return issue != null && issue.assigneeAgentId === agentId;
}

Try / catch

try {
  await runnerGoals.act(companyId, issueId, req);
} catch (e) {
  if (e instanceof RunnerGoalActionError && e.code === 'agent_not_assigned') {
    // refresh assignment or reassign, then retry with issue.assigneeAgentId
  } else throw e;
}

Prevention

When it happens

Trigger: Calling POST session-goal act (RunnerGoalActionRequest) with an agentId that exists in the binding but is not `issue.assigneeAgentId` — e.g. a non-assignee agent trying to pause/resume/clear/set goals on an issue assigned to a different agent.

Common situations: Board or tooling sends the wrong agent id; an issue was reassigned to another agent after the session was created; automated scripts iterate over agents and hit issues they don't own.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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