{"record":{"id":"6c6da14e819eaeb1","repo":"paperclipai/paperclip","slug":"policy-message","errorCode":null,"errorMessage":"${policy.message}","messagePattern":"\\$\\{policy\\.message\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugins/plugin-llm-wiki/src/wiki/core.ts","lineNumber":990,"sourceCode":"    for (const key of profile.sourceScopes.flatMap(scopeIdentity)) {\n      if (own.has(key)) overlaps += 1;\n    }\n  }\n  return overlaps;\n}\n\nasync function validatePaperclipIngestionProfile(ctx: PluginContext, input: {\n  companyId: string;\n  space: WikiSpace;\n  profile: PaperclipIngestionProfileV1;\n}) {\n  const policy = evaluatePaperclipProfilePolicy({\n    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      }","sourceCodeStart":972,"sourceCodeEnd":1008,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/plugin-llm-wiki/src/wiki/core.ts#L972-L1008","documentation":"Thrown by validatePaperclipIngestionProfile() when evaluatePaperclipProfilePolicy() denies a profile_update. The message is the policy's own message, naming the reason (archived_space, restricted_space, profile_disabled, profile_empty). Note requireEnabledProfile here is computed as profile.enabled && space.slug !== default, so enabling a profile on a non-default space with no source scopes triggers profile_empty.","triggerScenarios":"Updating a profile on an archived or non-shared space. Enabling a profile on a custom space, or submitting a profile with enabled=true but zero sourceScopes on a non-default space.","commonSituations":"UI 'Enable' toggle clicked before any source scope added. Profile update sent for a space that was archived concurrently. Access scope changed to private mid-session.","solutions":["Read policy.message to identify the reason and address it (un-archive, set accessScope=shared, add a source scope, or disable the profile before saving).","If enabling, ensure at least one source scope is configured first (see error 475).","Use the default space to bypass the enabled-profile gate during initial setup."],"exampleFix":"// before\nawait validatePaperclipIngestionProfile(ctx, { companyId, space: customSpace, profile: { enabled: true, sourceScopes: [], ... } });\n// after\nawait validatePaperclipIngestionProfile(ctx, { companyId, space: customSpace, profile: { enabled: true, sourceScopes: [{ kind: \"selected_projects\", projectIds: [\"p1\"] }], ... } });","handlingStrategy":"validation","validationCode":"const profile = await getPaperclipIngestionProfile(ctx, { companyId, wikiId, spaceSlug });\nif (profile.effectiveState === \"policy_blocked\") {\n  throw new Error(`Profile update blocked: ${profile.policyBlocks.join(\"; \")}`);\n}","typeGuard":"function isProfileUpdatable(p: { effectiveState: string }): boolean {\n  return p.effectiveState !== \"policy_blocked\";\n}","tryCatchPattern":"try {\n  await validatePaperclipIngestionProfile(ctx, { companyId, space, profile });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Paperclip ingestion policy denied\")) {\n    // read reason, un-archive space / set shared / add scopes, then retry\n  } else throw err;\n}","preventionTips":["Check policyBlocks via getPaperclipIngestionProfile() before updating.","Ensure the target space is active and shared before profile edits.","Add at least one source scope before enabling on a non-default space."],"tags":["llm-wiki","policy","profile","paperclip"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}