{"record":{"id":"227fa6e6091ec101","repo":"paperclipai/paperclip","slug":"projectid-or-rootissueid-is-required","errorCode":null,"errorMessage":"projectId or rootIssueId is required","messagePattern":"projectId or rootIssueId is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugins/plugin-llm-wiki/src/wiki/core.ts","lineNumber":3338,"sourceCode":"export function getDistillationAutoApplyRestriction(): DistillationAutoApplyRestriction {\n  const rawMode = process.env.PAPERCLIP_DEPLOYMENT_MODE;\n  const rawExposure = process.env.PAPERCLIP_DEPLOYMENT_EXPOSURE;\n  const deploymentMode =\n    rawMode === \"local_trusted\" || rawMode === \"authenticated\" ? rawMode : null;\n  const deploymentExposure =\n    rawExposure === \"private\" || rawExposure === \"public\" ? rawExposure : null;\n  const blocked = deploymentMode === \"authenticated\" && deploymentExposure === \"public\";\n  return {\n    autoApplyAllowed: !blocked,\n    autoApplyRestriction: blocked ? PUBLIC_DISTILLATION_AUTO_APPLY_RESTRICTION : null,\n    deploymentMode,\n    deploymentExposure,\n  };\n}\n\nexport async function distillPaperclipProjectPage(ctx: PluginContext, input: PaperclipProjectPageDistillationInput) {\n  if (!input.projectId && !input.rootIssueId) {\n    throw new Error(\"projectId or rootIssueId is required\");\n  }\n  const wikiId = normalizeWikiId(input.wikiId);\n  assertPaperclipSourceScopePayload(input);\n  const space = await requirePaperclipIngestionPolicy(ctx, { companyId: input.companyId, wikiId, spaceSlug: input.spaceSlug }, \"execute\", { requireEnabledProfile: true });\n  const scope = paperclipCursorScopeMetadata(input);\n  const issues = await listPaperclipBundleIssues(ctx, input);\n  const project = scope.projectId ? await ctx.projects.get(scope.projectId, input.companyId) : null;\n  const rootIssue = scope.rootIssueId ? await ctx.issues.get(scope.rootIssueId, input.companyId) : null;\n  const slug = projectPageSlug({ project, rootIssue });\n  const projectDir = `wiki/projects/${slug}`;\n  const standupPath = assertPagePath(`${projectDir}/standup.md`);\n  const pagePath = assertPagePath(`${projectDir}/index.md`);\n  const run = await createPaperclipDistillationRun(ctx, input);\n  const bundle = run.bundle;\n  const current = await readCurrentWithHash(ctx, input.companyId, pagePath, space);\n  assertExpectedHash(input.expectedProjectPageHash, current.hash, pagePath);\n\n  if (!hasDurableSignal(bundle, issues)) {","sourceCodeStart":3320,"sourceCodeEnd":3356,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/plugin-llm-wiki/src/wiki/core.ts#L3320-L3356","documentation":"Thrown by distillPaperclipProjectPage when neither input.projectId nor input.rootIssueId is provided. The function renders a project wiki page (index.md/standup.md) and needs a concrete scope to name the slug and directory. The guard fires before policy evaluation so the request fails fast.","triggerScenarios":"Calling distillPaperclipProjectPage with both projectId and rootIssueId absent or empty, e.g. a distill worker action that did not forward the scope params.","commonSituations":"A worker action param mapping drops projectId; an issue-driven distillation run where the source issue is not attached to a project and has no root-issue lineage; misconfigured routine passing only companyId.","solutions":["Pass projectId (preferred) or rootIssueId on every distillPaperclipProjectPage input.","For distillation triggered from an issue, resolve the issue's projectId from ctx.issues.get and forward it.","Validate the scope at the caller (worker action) before invoking the core function so users get a clearer upstream error."],"exampleFix":"// before\ndistillPaperclipProjectPage(ctx, { companyId, wikiId, spaceSlug });\n// after\ndistillPaperclipProjectPage(ctx, { companyId, wikiId, spaceSlug, projectId });","handlingStrategy":"validation","validationCode":"function assertProjectPageScope(input: { projectId?: string | null; rootIssueId?: string | null }) {\n  if (!input.projectId && !input.rootIssueId) throw new Error('projectId or rootIssueId is required');\n}","typeGuard":"function hasProjectPageScope(input: unknown): input is { projectId: string } | { rootIssueId: string } {\n  return !!input && typeof input === 'object' && (\n    !!(input as any).projectId || !!(input as any).rootIssueId\n  );\n}","tryCatchPattern":"try {\n  await distillPaperclipProjectPage(ctx, input);\n} catch (err) {\n  if (err instanceof Error && err.message === 'projectId or rootIssueId is required') {\n    // resolve projectId from the issue and retry\n  } else throw err;\n}","preventionTips":["Resolve projectId from the triggering issue before distillation.","Forward scope params through every worker action that calls distillPaperclipProjectPage."],"tags":["paperclip","distillation","validation"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}