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
- Call resolve_review with a concrete decision before reporting completion.
- If the work cannot be reviewed, report with reportedWorkDisposition: 'blocked' and a concrete blocker instead of done.
- Inspect the run's contextSnapshot for a review assignment early in the run and handle it before attempting completion.
- 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
- Check the context snapshot for review assignments at run start, not at completion time.
- Always invoke resolve_review with a decision before any completion report.
- Use reportedWorkDisposition 'blocked' with a concrete blocker when the review cannot be done.
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
- needs_review requires a concrete decision and a named…
- The done report includes unfinished work, failed…
- capability_live_attempt_active_turn
- capability_live_attempt_not_running
- Capability live session already has an active turn
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)