{"record":{"id":"8aac09a586212071","repo":"paperclipai/paperclip","slug":"paperclip-source-scope-must-specify-either-project","errorCode":null,"errorMessage":"Paperclip source scope must specify either projectId or rootIssueId, not both.","messagePattern":"Paperclip source scope must specify either projectId or rootIssueId, not both\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugins/plugin-llm-wiki/src/wiki/core.ts","lineNumber":481,"sourceCode":"  const space = await resolveSpace(ctx, {\n    companyId: input.companyId,\n    wikiId: input.wikiId,\n    spaceSlug: input.spaceSlug,\n  });\n  const profile = await profileForSpace(ctx, input.companyId, space);\n  const decision = evaluatePaperclipProfilePolicy({\n    space,\n    profile,\n    purpose,\n    requireEnabledProfile: options.requireEnabledProfile,\n  });\n  if (!decision.allowed) throw new Error(decision.message);\n  return decision.space;\n}\n\nfunction assertPaperclipSourceScopePayload(input: { projectId?: string | null; rootIssueId?: string | null }) {\n  if (input.projectId && input.rootIssueId) {\n    throw new Error(\"Paperclip source scope must specify either projectId or rootIssueId, not both.\");\n  }\n}\n\nfunction assertRequestedCharacterLimit(name: string, value: unknown, max: number) {\n  if (value == null) return;\n  if (typeof value !== \"number\" || !Number.isFinite(value) || value < 1) {\n    throw new Error(`${name} must be a positive number.`);\n  }\n  if (Math.floor(value) > max) {\n    throw new Error(`${name} exceeds the hard Paperclip ingestion cap of ${max} characters.`);\n  }\n}\n\nfunction stableSpaceId(input: { companyId: string; wikiId: string; slug: string }): string {\n  const hex = createHash(\"md5\")\n    .update(`${input.companyId}:${input.wikiId}:${input.slug}`)\n    .digest(\"hex\");\n  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;","sourceCodeStart":463,"sourceCodeEnd":499,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/plugin-llm-wiki/src/wiki/core.ts#L463-L499","documentation":"Thrown by assertPaperclipSourceScopePayload() when a Paperclip source scope payload specifies BOTH projectId and rootIssueId. A source scope is exclusive: it scopes either to a single project (selected_projects) or to a set of root issues (root_issues), never both. The guard rejects ambiguous payloads before they are normalized.","triggerScenarios":"Submitting a source scope payload with both fields populated (e.g. { projectId: \"p1\", rootIssueId: \"i1\" }). API client that builds the payload by merging two partial configs.","commonSituations":"Frontend form allowing both selectors to be filled. Migration code that unioned fields from two legacy shapes. Confused API consumer assuming both are required.","solutions":["Set exactly one of projectId or rootIssueId on the scope payload — leave the other null/undefined.","In the UI, make the project and root-issue selectors mutually exclusive.","Validate the payload with a schema that uses oneOf/exclusiveProperties before sending."],"exampleFix":"// before\nconst scope = { projectId: \"p1\", rootIssueId: \"i1\" }; // throws\n// after\nconst scope = { projectId: \"p1\" }; // project-scoped","handlingStrategy":"validation","validationCode":"function assertScopePayload(p: { projectId?: string | null; rootIssueId?: string | null }) {\n  if (p.projectId && p.rootIssueId) {\n    throw new Error(\"Specify either projectId or rootIssueId, not both.\");\n  }\n}","typeGuard":"function isExclusiveScopePayload(p: { projectId?: string | null; rootIssueId?: string | null }): boolean {\n  return !(p.projectId && p.rootIssueId);\n}","tryCatchPattern":null,"preventionTips":["Make projectId and rootIssueId mutually exclusive in the UI.","Validate with a oneOf schema at the API boundary.","Initialize unused scope field to null, never to an empty string that could be truthy."],"tags":["llm-wiki","validation","paperclip","source-scope"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}