{"record":{"id":"d5e4b21430d64106","repo":"paperclipai/paperclip","slug":"completion-report-has-no-bound-task","errorCode":null,"errorMessage":"Completion report has no bound task.","messagePattern":"Completion report has no bound task\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/native-completion-feedback.ts","lineNumber":33,"sourceCode":"} from \"@paperclipai/db\";\nimport {\n  normalizePrpResultSignals,\n  type PrpStructuredRunResult,\n} from \"../../vendor/paperclip-runner/index.js\";\n\n/** Read current constraints before accepting the report, not a premature status commit. */\nexport async function nativeCompletionFeedback(\n  db: Db,\n  runId: string,\n  result: PrpStructuredRunResult,\n): Promise<string> {\n  const run = await db\n    .select()\n    .from(heartbeatRuns)\n    .where(eq(heartbeatRuns.id, runId))\n    .then((rows) => rows[0]);\n  if (!run?.nativeIssueId)\n    throw new Error(\"Completion report has no bound task.\");\n  const issue = await db\n    .select()\n    .from(issues)\n    .where(\n      and(\n        eq(issues.id, run.nativeIssueId),\n        eq(issues.companyId, run.companyId),\n      ),\n    )\n    .then((rows) => rows[0]);\n  if (!issue) throw new Error(\"Completion task no longer exists.\");\n  const reviewContext = readNativeReviewAssignmentContext(run.contextSnapshot);\n  if (reviewContext) {\n    const review = await getNativeReviewAssignment(db, {\n      companyId: run.companyId, issueId: issue.id, agentId: run.agentId,\n      contextSnapshot: reviewContext, allowResolvedByRunId: run.id,\n    });\n    if (review?.interaction.status === \"pending\" && result.reportedWorkDisposition !== \"blocked\") {","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/native-runtime/native-completion-feedback.ts#L15-L51","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the runId exists in heartbeatRuns and its nativeIssueId column is set before reporting completion.","Re-create the run through the native flow so it gets a bound nativeIssueId.","Check you're pointing at the right environment/database (dev vs prod run IDs are not interchangeable).","If the run row was purged, start a new run instead of reporting against the orphaned ID."],"exampleFix":"// before\nawait reportCompletion({ runId: cachedRunId, result });\n// after\nconst run = await getRun(cachedRunId);\nif (!run?.nativeIssueId) throw new Error(`run ${cachedRunId} is not bound to a task; start a new run`);\nawait reportCompletion({ runId: cachedRunId, result });","handlingStrategy":"validation","validationCode":"const run = await getRun(runId);\nif (!run) throw new Error(`run ${runId} not found`);\nif (!run.nativeIssueId) throw new Error(`run ${runId} has no bound native task; cannot report completion`);","typeGuard":"const isReportableRun = (r: { id: string; nativeIssueId?: string | null } | undefined): r is { id: string; nativeIssueId: string } => !!r && typeof r.nativeIssueId === \"string\" && r.nativeIssueId.length > 0;","tryCatchPattern":"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; }","preventionTips":["Only report completion for runs created through the native-issue flow.","Never cache run IDs across DB resets or environments.","Check run.nativeIssueId before queuing completion reports."],"tags":["database","state","completion-report"],"backgroundTag":"record-not-found","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}