paperclipai/paperclip · error

Completion report has no bound task.

Error message

Completion report has no bound task.

What it means

nativeCompletionFeedback loads the heartbeat run for the reported runId and requires it to have a nativeIssueId. When the run row doesn't exist or has no bound native issue, the completion report cannot be attributed to any task and this error is thrown. It is a control-plane invariant: every completion report must map to a native task.

Solutions

  1. Verify the runId exists in heartbeatRuns and its nativeIssueId column is set before reporting completion.
  2. Re-create the run through the native flow so it gets a bound nativeIssueId.
  3. Check you're pointing at the right environment/database (dev vs prod run IDs are not interchangeable).
  4. If the run row was purged, start a new run instead of reporting against the orphaned ID.

Example fix

// before
await reportCompletion({ runId: cachedRunId, result });
// after
const run = await getRun(cachedRunId);
if (!run?.nativeIssueId) throw new Error(`run ${cachedRunId} is not bound to a task; start a new run`);
await reportCompletion({ runId: cachedRunId, result });
Defensive patterns

Strategy: validation

Validate before calling

const run = await getRun(runId);
if (!run) throw new Error(`run ${runId} not found`);
if (!run.nativeIssueId) throw new Error(`run ${runId} has no bound native task; cannot report completion`);

Type guard

const isReportableRun = (r: { id: string; nativeIssueId?: string | null } | undefined): r is { id: string; nativeIssueId: string } => !!r && typeof r.nativeIssueId === "string" && r.nativeIssueId.length > 0;

Try / catch

try { await reportCompletion({ runId, result }); } catch (e) { if (e.message === "Completion report has no bound task.") { /* start a new bound run or drop the report */ } else throw e; }

Prevention

When it happens

Trigger: Reporting completion with a runId whose heartbeatRuns row was deleted; a run created outside the native-issue flow (nativeIssueId never set); a typo'd or from-another-environment runId.

Common situations: Agent adapters caching run IDs across DB resets; runs created by older versions before the native binding was introduced; local dev databases wiped while agents still reference old run IDs.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

} from "@paperclipai/db";
import {
  normalizePrpResultSignals,
  type PrpStructuredRunResult,
} from "../../vendor/paperclip-runner/index.js";

/** Read current constraints before accepting the report, not a premature status commit. */
export async function nativeCompletionFeedback(
  db: Db,
  runId: string,
  result: PrpStructuredRunResult,
): Promise<string> {
  const run = await db
    .select()
    .from(heartbeatRuns)
    .where(eq(heartbeatRuns.id, runId))
    .then((rows) => rows[0]);
  if (!run?.nativeIssueId)
    throw new Error("Completion report has no bound task.");
  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") {

View on GitHub (pinned to 3f1d897a7c)