{"record":{"id":"a715b7c5a08506d8","repo":"paperclipai/paperclip","slug":"selected-projects-exceeds-the-hard-cap-of-max-pa","errorCode":null,"errorMessage":"selected_projects exceeds the hard cap of ${MAX_PAPERCLIP_PROFILE_SELECTED_PROJECTS}.","messagePattern":"selected_projects exceeds the hard cap of (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugins/plugin-llm-wiki/src/wiki/core.ts","lineNumber":1003,"sourceCode":"    space: input.space,\n    profile: input.profile,\n    purpose: \"profile_update\",\n    requireEnabledProfile: input.profile.enabled && input.space.slug !== DEFAULT_SPACE_SLUG,\n  });\n  if (!policy.allowed) throw new Error(policy.message);\n  if (input.profile.enabled && input.profile.sourceScopes.length === 0) {\n    throw new Error(\"Paperclip ingestion profile must include at least one source scope before it can be enabled.\");\n  }\n  if (input.profile.sourceScopes.length > MAX_PAPERCLIP_INGESTION_PROFILE_SOURCE_COUNT) {\n    throw new Error(`Paperclip ingestion profile sources exceed the hard cap of ${MAX_PAPERCLIP_INGESTION_PROFILE_SOURCE_COUNT}.`);\n  }\n  for (const scope of input.profile.sourceScopes) {\n    if (scope.kind === \"company_all\" && input.space.slug !== DEFAULT_SPACE_SLUG) {\n      throw new Error(\"Everything in the company is only available on the default wiki space.\");\n    }\n    if (scope.kind === \"selected_projects\") {\n      if (scope.projectIds.length > MAX_PAPERCLIP_PROFILE_SELECTED_PROJECTS) {\n        throw new Error(`selected_projects exceeds the hard cap of ${MAX_PAPERCLIP_PROFILE_SELECTED_PROJECTS}.`);\n      }\n      for (const projectId of scope.projectIds) {\n        const project = await ctx.projects.get(projectId, input.companyId);\n        if (!project) throw new Error(`Project belongs to another company or does not exist: ${projectId}`);\n      }\n    }\n    if (scope.kind === \"root_issues\") {\n      if (scope.issueIds.length > MAX_PAPERCLIP_PROFILE_ROOT_ISSUES) {\n        throw new Error(`root_issues exceeds the hard cap of ${MAX_PAPERCLIP_PROFILE_ROOT_ISSUES}.`);\n      }\n      for (const issueId of scope.issueIds) {\n        const issue = await ctx.issues.get(issueId, input.companyId);\n        if (!issue) throw new Error(`Issue belongs to another company or does not exist: ${issueId}`);\n      }\n    }\n  }\n}\n","sourceCodeStart":985,"sourceCodeEnd":1021,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/plugin-llm-wiki/src/wiki/core.ts#L985-L1021","documentation":"Thrown by validatePaperclipIngestionProfile when a 'selected_projects' source scope lists more project ids than MAX_PAPERCLIP_PROFILE_SELECTED_PROJECTS (currently 25). It is a hard guard against unbounded ingestion fan-out per scope. The check runs inside updatePaperclipIngestionProfile, so any profile write (UI settings save or API call) that exceeds the cap is rejected before persistence.","triggerScenarios":"Calling updatePaperclipIngestionProfile (or the settings route behind it) with profile.sourceScopes containing a scope of kind 'selected_projects' whose projectIds array has more than 25 entries. Each scope is checked independently, so even one oversized scope trips it.","commonSituations":"Bulk-selecting an entire project portfolio in the wiki ingestion picker; copying a large project list from elsewhere; scripting profile updates without chunking; treating the picker as unlimited.","solutions":["Trim scope.projectIds to at most 25 entries before submitting the profile update.","Split excess projects into multiple spaces or multiple 'selected_projects' scopes (note the profile-wide MAX_PAPERCLIP_INGESTION_PROFILE_SOURCE_COUNT=3 cap also applies).","If you genuinely need company-wide coverage, switch the scope kind to 'company_all' on the default space instead of enumerating projects.","Re-fetch candidates via listPaperclipIngestionCandidates to confirm which project ids are valid before resubmitting."],"exampleFix":"// before\nprofile.sourceScopes = [{ kind: \"selected_projects\", projectIds: allProjectIds }]; // 80 ids\n\n// after\nprofile.sourceScopes = [{ kind: \"selected_projects\", projectIds: allProjectIds.slice(0, 25) }];","handlingStrategy":"validation","validationCode":"const MAX_SELECTED_PROJECTS = 25;\nfunction sanitizeSelectedProjectsScope(scope) {\n  if (scope.kind !== \"selected_projects\") return scope;\n  if (scope.projectIds.length > MAX_SELECTED_PROJECTS) {\n    throw new RangeError(`selected_projects cap is ${MAX_SELECTED_PROJECTS}`);\n  }\n  return scope;\n}\n// run before updatePaperclipIngestionProfile:\nprofile.sourceScopes.forEach(sanitizeSelectedProjectsScope);","typeGuard":"function isUnderSelectedProjectsCap(scope, cap = 25) {\n  return scope.kind === \"selected_projects\" ? scope.projectIds.length <= cap : true;\n}","tryCatchPattern":"try {\n  await updatePaperclipIngestionProfile(ctx, { companyId, spaceSlug, profile });\n} catch (err) {\n  if (/selected_projects exceeds the hard cap/.test(err.message)) {\n    profile.sourceScopes = profile.sourceScopes.map((s) =>\n      s.kind === \"selected_projects\" ? { ...s, projectIds: s.projectIds.slice(0, 25) } : s);\n    return updatePaperclipIngestionProfile(ctx, { companyId, spaceSlug, profile });\n  }\n  throw err;\n}","preventionTips":["Cap selected project lists in the UI picker at 25.","Prefer 'company_all' on the default space over enumerating many projects.","Remember the profile-wide source count cap (3) when splitting into multiple scopes."],"tags":["validation","ingestion","wiki","limits","paperclip"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}