{"record":{"id":"0889bb4ea59d83f1","repo":"paperclipai/paperclip","slug":"backfill-work-items-must-target-a-projectid-or-roo","errorCode":null,"errorMessage":"Backfill work items must target a projectId or rootIssueId; whole-company backfill is not allowed.","messagePattern":"Backfill work items must target a projectId or rootIssueId; whole-company backfill is not allowed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugins/plugin-llm-wiki/src/wiki/core.ts","lineNumber":2904,"sourceCode":"          AND id = $6`,\n      [input.companyId, wikiId, input.runId, input.sourceWindowEnd, input.sourceHash, input.cursorId, space.id],\n    );\n  }\n\n  return {\n    status: input.status,\n    cursorAdvanced: input.status === \"succeeded\" && Boolean(input.cursorId && input.sourceHash && input.sourceWindowEnd),\n  };\n}\n\nexport async function createPaperclipDistillationWorkItem(ctx: PluginContext, input: PaperclipDistillationWorkItemInput) {\n  const wikiId = normalizeWikiId(input.wikiId);\n  assertPaperclipSourceScopePayload(input);\n  const space = await requirePaperclipIngestionPolicy(ctx, { companyId: input.companyId, wikiId, spaceSlug: input.spaceSlug }, \"queue\", { requireEnabledProfile: true });\n  const itemId = randomUUID();\n  const scope = paperclipCursorScopeMetadata(input);\n  if (input.kind === \"backfill\" && !scope.projectId && !scope.rootIssueId) {\n    throw new Error(\"Backfill work items must target a projectId or rootIssueId; whole-company backfill is not allowed.\");\n  }\n  await ctx.db.execute(\n    `INSERT INTO ${distillationWorkItemTable(ctx)} AS paperclip_distillation_work_items\n       (id, company_id, wiki_id, space_id, work_item_kind, status, priority, project_id, root_issue_id, requested_by_issue_id, idempotency_key, metadata)\n     VALUES ($1, $2, $3, $11, $4, 'pending', $5, $6, $7, $8, $9, $10::jsonb)\n     ON CONFLICT (company_id, wiki_id, space_id, idempotency_key)\n     DO UPDATE SET priority = EXCLUDED.priority,\n                   metadata = paperclip_distillation_work_items.metadata || EXCLUDED.metadata,\n                   updated_at = now()`,\n    [\n      itemId,\n      input.companyId,\n      wikiId,\n      input.kind,\n      input.priority ?? \"medium\",\n      scope.projectId,\n      scope.rootIssueId,\n      input.requestedByIssueId ?? null,","sourceCodeStart":2886,"sourceCodeEnd":2922,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/plugin-llm-wiki/src/wiki/core.ts#L2886-L2922","documentation":"Thrown by createPaperclipDistillationWorkItem when kind === 'backfill' but the resolved scope has neither projectId nor rootIssueId. Backfill must target a single project or root-issue subtree; whole-company backfill is intentionally disallowed to keep distillation cost and cursor advancement bounded. The scope is derived by paperclipCursorScopeMetadata(input).","triggerScenarios":"Calling createPaperclipDistillationWorkItem with kind:'backfill' while leaving projectId and rootIssueId null/empty, e.g. via the backfill-paperclip-distillation worker action without those params.","commonSituations":"Operator tries to 'backfill everything' after enabling distillation for a company; a script iterates companies but forgets to scope per project; rootIssueId was passed under the wrong param key.","solutions":["Provide exactly one of projectId or rootIssueId on the backfill request.","If you need company-wide coverage, enumerate projects via ctx.projects.list and submit one backfill work item per projectId.","For issue-tree backfill, pass rootIssueId of the top-level issue, not a leaf issue id."],"exampleFix":"// before\ncreatePaperclipDistillationWorkItem(ctx, { companyId, kind: 'backfill' });\n// after\ncreatePaperclipDistillationWorkItem(ctx, { companyId, kind: 'backfill', projectId });","handlingStrategy":"validation","validationCode":"function assertBackfillScope(input: { projectId?: string | null; rootIssueId?: string | null }) {\n  if (!input.projectId && !input.rootIssueId) {\n    throw new Error('Backfill requires projectId or rootIssueId');\n  }\n}","typeGuard":"function hasBackfillScope(input: unknown): input is { projectId: string } | { rootIssueId: string } {\n  return !!input && typeof input === 'object' && (\n    (typeof (input as any).projectId === 'string' && (input as any).projectId.length > 0) ||\n    (typeof (input as any).rootIssueId === 'string' && (input as any).rootIssueId.length > 0)\n  );\n}","tryCatchPattern":"try {\n  await createPaperclipDistillationWorkItem(ctx, { companyId, kind: 'backfill', projectId, rootIssueId });\n} catch (err) {\n  if (err instanceof Error && /whole-company backfill/.test(err.message)) {\n    // enumerate projects and submit one backfill each\n  } else throw err;\n}","preventionTips":["Always scope backfill to a project or root issue.","Validate scope presence at the worker action layer before calling the core function."],"tags":["paperclip","distillation","backfill","validation"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}