paperclipai/paperclip · error

Completion cites no registered attachment on this task. Use…

Error message

Completion cites no registered attachment on this task. Use register_deliverable for the requested file and cite deliverable:<attachmentId> from its receipt. No human completion approval was created.

What it means

validateNativeDeliverableEvidence checks that a completion citing deliverable:<attachmentId> points to a registered issue attachment on the same task and company. When the cited attachment ID does not resolve to any issueAttachments row joined to its asset, the runtime rejects it and clarifies that no human completion approval was created as a side effect. This prevents fabricated deliverable citations.

Solutions

  1. Run register_deliverable for the requested file and cite the deliverable:<attachmentId> exactly as printed in its receipt
  2. Verify the cited attachment belongs to this task (issueId) and company
  3. If the file cannot be registered, report the concrete blocker instead of citing an unregistered attachment

Example fix

// before
Deliverable: deliverable:42
// after
Deliverable: deliverable:9c1e8b4d-4a2f-4c1e-8b4d-4a2f4c1e8b4d // from register_deliverable receipt
Defensive patterns

Strategy: validation

Validate before calling

const receipt = await registerDeliverable(file);
const cited = receipt.attachmentId; // cite deliverable:<cited> exactly; never invent IDs
if (!cited) throw new Error("register_deliverable did not return an attachmentId; do not cite a deliverable.");

Type guard

const resolvableCitation = (id, knownReceipts) => knownReceipts.some(r => r.attachmentId === id && r.issueId === issueId);

Try / catch

try { await feedback(payload); } catch (e) { if (e.message.includes("no registered attachment")) { /* re-run register_deliverable and cite the returned deliverable:<id> */ } else throw e; }

Prevention

When it happens

Trigger: nativeCompletionFeedback / verifyReceipt referencing deliverable:<id> where the id is hallucinated, from a different issue/company, or where register_deliverable was never actually called for the requested file.

Common situations: Model invents an attachment ID instead of reading the receipt returned by register_deliverable; copy-pasting an attachment ID from a different task; register_deliverable failed earlier and the model cites the intended-but-unregistered ID.

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/7af78837eb791e51. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/native-runtime/native-deliverable-feedback.ts:116

    ...result.completionClaim.criteria.flatMap(({ evidenceRefs }) => evidenceRefs),
  ]);
  let registeredAttachment = false;
  for (const value of refs) {
    if (typeof value !== "string") continue;
    const ref = value.trim();
    const attachmentPath = /^\/api\/attachments\/([^/?#]+)\/content(?:[?#].*)?$/u.exec(ref);
    if (ref.startsWith("deliverable:") || attachmentPath) {
      const id = attachmentPath?.[1] ?? ref.slice("deliverable:".length);
      const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
      const [attachment] = uuid.test(id)
        ? await db.select({ id: issueAttachments.id, originatingRunId: issueAttachments.originatingRunId,
            filename: assets.originalFilename, byteSize: assets.byteSize, sha256: assets.sha256 }).from(issueAttachments)
            .innerJoin(assets, and(eq(assets.id, issueAttachments.assetId), eq(assets.companyId, binding.companyId)))
            .where(and(eq(issueAttachments.id, id), eq(issueAttachments.companyId, binding.companyId), eq(issueAttachments.issueId, binding.issueId)))
            .limit(1)
        : [];
      if (!attachment) {
        throw new Error("Completion cites no registered attachment on this task. Use register_deliverable for the requested file and cite deliverable:<attachmentId> from its receipt. No human completion approval was created.");
      }
      // A prior output (or user input) can be useful context, but does not prove
      // this run published the newly requested output. The receipt survives a
      // controller restart of this run; a replacement can re-register preserved
      // workspace bytes internally rather than asking the user to confirm them.
      if (fileRequested && attachment.originatingRunId !== binding.runId) continue;
      if (fileRequested && !await hasCurrentPublicationReceipt(db, binding.companyId, binding.semanticToolReceipts, attachment)) {
        throw new Error("This attachment has no matching verified publication receipt for this run's requested output. Inspect any preserved file and use register_deliverable to verify its current filename, size, and SHA-256, then cite the new receipt. No human completion approval was created.");
      }
      registeredAttachment = true;
      continue;
    }
    // URLs and typed durable refs are not workspace paths. Verification commands
    // belong in verification; do not scan prose or upload files named by a model.
    const localFile = /^(?:file:|\.{0,2}\/|[a-z]:[\\/])/iu.test(ref)
      || (!/^[a-z][a-z0-9+.-]*:/iu.test(ref) && /^[^\r\n]+\.[a-z0-9]{1,16}(?::\d+(?::\d+)?)?$/iu.test(ref));
    if (localFile && (fileRequested || artifactRefs.has(value))) {
      throw new Error("Completion cites a workspace-only file that the user cannot download. Before finishing, use register_deliverable for requested file outputs and cite deliverable:<attachmentId> from the receipt, with /api/attachments/<attachmentId>/content as the download link. For repository changes, cite an accessible PR or registered work product instead. No human completion approval was created.");

View on GitHub (pinned to 3f1d897a7c)