paperclipai/paperclip · error

Resolve your assigned review with resolve_review before…

Error message

Resolve your assigned review with resolve_review before finishing. If you cannot review the work, report the concrete blocker with paperclip_block.

What it means

When the run's context snapshot contains a review assignment, the agent must resolve that review before the task can complete. nativeCompletionFeedback throws if the review interaction is still pending and the agent did not report a 'blocked' work disposition. This enforces the review gate: a pending reviewer cannot just report done.

Solutions

  1. Call resolve_review with a concrete decision before reporting completion.
  2. If the work cannot be reviewed, report with reportedWorkDisposition: 'blocked' and a concrete blocker instead of done.
  3. Inspect the run's contextSnapshot for a review assignment early in the run and handle it before attempting completion.
  4. Check the resolve_review call actually succeeded and persisted (interaction status no longer 'pending').

Example fix

// before
await reportCompletion({ runId, result: { reportedWorkDisposition: "done" } });
// after
if (hasPendingReview(contextSnapshot)) {
  await resolveReview({ runId, decision: "approve" }); // or report blocked
}
await reportCompletion({ runId, result: { reportedWorkDisposition: "done" } });
Defensive patterns

Strategy: validation

Validate before calling

const reviewCtx = readNativeReviewAssignmentContext(run.contextSnapshot);
if (reviewCtx && review.interaction.status === "pending" && disposition !== "blocked") {
  throw new Error("resolve the pending review (resolve_review) or report blocked before completing");
}

Type guard

const hasPendingReview = (s: unknown): boolean => { const ctx = readNativeReviewAssignmentContext(s); return !!ctx && ctx.interaction.status === "pending"; };

Try / catch

try { await reportCompletion({ runId, result }); } catch (e) { if (e.message.startsWith("Resolve your assigned review")) { await resolveReview({ runId, decision: /* concrete decision */ }); return reportCompletion({ runId, result }); } throw e; }

Prevention

When it happens

Trigger: An agent with an assigned review calls the completion/feedback API with reportedWorkDisposition != 'blocked' while the review interaction status is 'pending'; resolve_review was never invoked or its result wasn't persisted before finishing.

Common situations: Reviewer agents skipping the resolve_review step; agent runs finishing on a timer/turn limit before resolving the review; adapters reporting done generically without checking for pending review interactions in the context snapshot.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/4acb39ce1623e251. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/native-runtime/native-completion-feedback.ts:52

  const issue = await db
    .select()
    .from(issues)
    .where(
      and(
        eq(issues.id, run.nativeIssueId),
        eq(issues.companyId, run.companyId),
      ),
    )
    .then((rows) => rows[0]);
  if (!issue) throw new Error("Completion task no longer exists.");
  const reviewContext = readNativeReviewAssignmentContext(run.contextSnapshot);
  if (reviewContext) {
    const review = await getNativeReviewAssignment(db, {
      companyId: run.companyId, issueId: issue.id, agentId: run.agentId,
      contextSnapshot: reviewContext, allowResolvedByRunId: run.id,
    });
    if (review?.interaction.status === "pending" && result.reportedWorkDisposition !== "blocked") {
      throw new Error("Resolve your assigned review with resolve_review before finishing. If you cannot review the work, report the concrete blocker with paperclip_block.");
    }
    return review?.interaction.status === "pending"
      ? "Review blocker recorded. Paperclip will preserve the task and record the reviewer recovery action."
      : "Review report accepted. The recorded review decision controls task completion; this report cannot override it.";
  }
  const signals = normalizePrpResultSignals(result);
  if (
    result.reportedWorkDisposition === "done" &&
    (!result.completionClaim.objectiveSatisfied ||
      result.completionClaim.criteria.some(
        (entry) => entry.status !== "satisfied",
      ) ||
      result.completionClaim.remainingWork.some(
        (entry) => entry.blocksCompletion,
      ) ||
      signals.verification.some((entry) => entry.status === "failed") ||
      signals.actionableAttentionRequests.length > 0)
  ) {

View on GitHub (pinned to 3f1d897a7c)