paperclipai/paperclip · error · Error
selected_projects exceeds the hard cap of ${MAX_PAPERCLIP_PR
Error message
selected_projects exceeds the hard cap of ${MAX_PAPERCLIP_PROFILE_SELECTED_PROJECTS}. What it means
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.
Source
Thrown at packages/plugins/plugin-llm-wiki/src/wiki/core.ts:1003
space: input.space,
profile: input.profile,
purpose: "profile_update",
requireEnabledProfile: input.profile.enabled && input.space.slug !== DEFAULT_SPACE_SLUG,
});
if (!policy.allowed) throw new Error(policy.message);
if (input.profile.enabled && input.profile.sourceScopes.length === 0) {
throw new Error("Paperclip ingestion profile must include at least one source scope before it can be enabled.");
}
if (input.profile.sourceScopes.length > MAX_PAPERCLIP_INGESTION_PROFILE_SOURCE_COUNT) {
throw new Error(`Paperclip ingestion profile sources exceed the hard cap of ${MAX_PAPERCLIP_INGESTION_PROFILE_SOURCE_COUNT}.`);
}
for (const scope of input.profile.sourceScopes) {
if (scope.kind === "company_all" && input.space.slug !== DEFAULT_SPACE_SLUG) {
throw new Error("Everything in the company is only available on the default wiki space.");
}
if (scope.kind === "selected_projects") {
if (scope.projectIds.length > MAX_PAPERCLIP_PROFILE_SELECTED_PROJECTS) {
throw new Error(`selected_projects exceeds the hard cap of ${MAX_PAPERCLIP_PROFILE_SELECTED_PROJECTS}.`);
}
for (const projectId of scope.projectIds) {
const project = await ctx.projects.get(projectId, input.companyId);
if (!project) throw new Error(`Project belongs to another company or does not exist: ${projectId}`);
}
}
if (scope.kind === "root_issues") {
if (scope.issueIds.length > MAX_PAPERCLIP_PROFILE_ROOT_ISSUES) {
throw new Error(`root_issues exceeds the hard cap of ${MAX_PAPERCLIP_PROFILE_ROOT_ISSUES}.`);
}
for (const issueId of scope.issueIds) {
const issue = await ctx.issues.get(issueId, input.companyId);
if (!issue) throw new Error(`Issue belongs to another company or does not exist: ${issueId}`);
}
}
}
}
View on GitHub (pinned to 67001ec6eb)
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.
Example fix
// before
profile.sourceScopes = [{ kind: "selected_projects", projectIds: allProjectIds }]; // 80 ids
// after
profile.sourceScopes = [{ kind: "selected_projects", projectIds: allProjectIds.slice(0, 25) }]; Defensive patterns
Strategy: validation
Validate before calling
const MAX_SELECTED_PROJECTS = 25;
function sanitizeSelectedProjectsScope(scope) {
if (scope.kind !== "selected_projects") return scope;
if (scope.projectIds.length > MAX_SELECTED_PROJECTS) {
throw new RangeError(`selected_projects cap is ${MAX_SELECTED_PROJECTS}`);
}
return scope;
}
// run before updatePaperclipIngestionProfile:
profile.sourceScopes.forEach(sanitizeSelectedProjectsScope); Type guard
function isUnderSelectedProjectsCap(scope, cap = 25) {
return scope.kind === "selected_projects" ? scope.projectIds.length <= cap : true;
} Try / catch
try {
await updatePaperclipIngestionProfile(ctx, { companyId, spaceSlug, profile });
} catch (err) {
if (/selected_projects exceeds the hard cap/.test(err.message)) {
profile.sourceScopes = profile.sourceScopes.map((s) =>
s.kind === "selected_projects" ? { ...s, projectIds: s.projectIds.slice(0, 25) } : s);
return updatePaperclipIngestionProfile(ctx, { companyId, spaceSlug, profile });
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- root_issues exceeds the hard cap of ${MAX_PAPERCLIP_PROFILE_
- Project belongs to another company or does not exist: ${proj
- Issue belongs to another company or does not exist: ${issueI
- Wiki page writes must target AGENTS.md, IDEA.md, or wiki/: $
- Wiki page path must be a markdown file: ${path}
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/a715b7c5a08506d8.
Report an issue: GitHub.