{"record":{"id":"3c209c9d9d9f5865","repo":"paperclipai/paperclip","slug":"paperclip-runner-file-handoff-origin-not-persisted","errorCode":"paperclip_runner_file_handoff_origin_not_persisted","errorMessage":"paperclip_runner_file_handoff_origin_not_persisted","messagePattern":"paperclip_runner_file_handoff_origin_not_persisted","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/native-runner-file-handoff.ts","lineNumber":1260,"sourceCode":"    ) {\n      throw new Error(\"paperclip_runner_file_handoff_storage_mismatch\");\n    }\n    const attachment = await issueService(input.db).createAttachment({\n      issueId: input.binding.issueId,\n      provider: stored.provider,\n      objectKey: stored.objectKey,\n      contentType: stored.contentType,\n      byteSize: stored.byteSize,\n      sha256: stored.sha256,\n      originalFilename: stored.originalFilename,\n      createdByAgentId: input.binding.agentId,\n      createdByRunId: input.binding.runId,\n    });\n    if (\n      attachment.originatingRunId !== input.binding.runId ||\n      !attachment.artifactWorkProductId\n    ) {\n      throw new Error(\"paperclip_runner_file_handoff_origin_not_persisted\");\n    }\n    await input.db\n      .update(issueWorkProducts)\n      .set({ title: verified.title, updatedAt: new Date() })\n      .where(\n        and(\n          eq(issueWorkProducts.id, attachment.artifactWorkProductId),\n          eq(issueWorkProducts.companyId, input.binding.companyId),\n          eq(issueWorkProducts.issueId, input.binding.issueId),\n          eq(issueWorkProducts.createdByRunId, input.binding.runId),\n        ),\n      );\n    const comment = await issueService(input.db).addComment(\n      input.binding.issueId,\n      `Prepared ${verified.title} for this response.`,\n      { agentId: input.binding.agentId, runId: input.binding.runId },\n      {\n        attachmentIds: [attachment.id],","sourceCodeStart":1242,"sourceCodeEnd":1278,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/native-runtime/native-runner-file-handoff.ts#L1242-L1278","documentation":"After createAttachment persists the deliverable, the code confirms the database round-trip: the returned attachment's originatingRunId must equal the binding runId and an artifactWorkProductId must have been created. If either is absent, the origin/run provenance was not persisted, so the idempotency and rollback machinery cannot work; the upload is rolled back (storage object deleted) and this error is thrown.","triggerScenarios":"issueService.createAttachment returns an attachment whose originatingRunId differs from input.binding.runId, or whose artifactWorkProductId is null/undefined — e.g. createAttachment ignoring createdByRunId, or a fork/older schema lacking the originating_run_id or artifact_work_product_id columns.","commonSituations":"Running server code against a database missing recent migrations that added originating_run_id or artifact work-product linkage; a modified createAttachment that drops run/agent metadata; schema drift between packages/db and the running server build; trims/defaults overwriting runId.","solutions":["Run pending database migrations (pnpm db:generate / apply) so issue_attachments.originating_run_id and the artifact work-product linkage exist and are populated.","Check the createAttachment implementation in server issue service: ensure createdByRunId is written to originatingRunId and an artifact work product is created in the same transaction.","Rebuild/align packages/db dist schema with the running server version; stale compiled schema can silently drop new columns.","Verify no DB triggers/defaults null out originating_run_id or artifact_work_product_id on insert."],"exampleFix":"// before (custom createAttachment)\nawait db.insert(issueAttachments).values({ issueId, provider, objectKey, contentType, byteSize, sha256, originalFilename, createdByAgentId });\n// after\nawait db.insert(issueAttachments).values({ issueId, provider, objectKey, contentType, byteSize, sha256, originalFilename, createdByAgentId, createdByRunId: runId, artifactWorkProductId: await ensureArtifactWorkProduct(...) });","handlingStrategy":"validation","validationCode":"// confirm the DB schema supports run provenance before enabling native file handoff\nconst cols = await db.execute(sql`SELECT column_name FROM information_schema.columns WHERE table_name = 'issue_attachments'`);\nconst names = cols.rows.map(r => r.column_name);\nif (!names.includes('originating_run_id')) throw new Error('run pending migration: originating_run_id missing');","typeGuard":null,"tryCatchPattern":"try {\n  await prepareNativeRunnerFileHandoff(input);\n} catch (e) {\n  if (e.message === 'paperclip_runner_file_handoff_origin_not_persisted') {\n    await runPendingMigrations(); // the catch path already deleted the uploaded object\n    return prepareNativeRunnerFileHandoff(input);\n  }\n  throw e;\n}","preventionTips":["Apply packages/db migrations before deploying server code that reads new columns.","Keep createAttachment writing createdByRunId and creating the artifact work product atomically.","Add a smoke test asserting attachment.originatingRunId === runId and artifactWorkProductId after creation.","Avoid custom forks of createAttachment that drop provenance fields."],"tags":["database","persistence","schema-migration","native-runner"],"backgroundTag":"internal-invariant-violation","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"}