paperclipai/paperclip · error · Error

${policy.message}

Error message

${policy.message}

What it means

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.

Source

Thrown at packages/plugins/plugin-llm-wiki/src/wiki/core.ts:990

    for (const key of profile.sourceScopes.flatMap(scopeIdentity)) {
      if (own.has(key)) overlaps += 1;
    }
  }
  return overlaps;
}

async function validatePaperclipIngestionProfile(ctx: PluginContext, input: {
  companyId: string;
  space: WikiSpace;
  profile: PaperclipIngestionProfileV1;
}) {
  const policy = evaluatePaperclipProfilePolicy({
    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}`);
      }

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. 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).
  2. If enabling, ensure at least one source scope is configured first (see error 475).
  3. Use the default space to bypass the enabled-profile gate during initial setup.

Example fix

// before
await validatePaperclipIngestionProfile(ctx, { companyId, space: customSpace, profile: { enabled: true, sourceScopes: [], ... } });
// after
await validatePaperclipIngestionProfile(ctx, { companyId, space: customSpace, profile: { enabled: true, sourceScopes: [{ kind: "selected_projects", projectIds: ["p1"] }], ... } });
Defensive patterns

Strategy: validation

Validate before calling

const profile = await getPaperclipIngestionProfile(ctx, { companyId, wikiId, spaceSlug });
if (profile.effectiveState === "policy_blocked") {
  throw new Error(`Profile update blocked: ${profile.policyBlocks.join("; ")}`);
}

Type guard

function isProfileUpdatable(p: { effectiveState: string }): boolean {
  return p.effectiveState !== "policy_blocked";
}

Try / catch

try {
  await validatePaperclipIngestionProfile(ctx, { companyId, space, profile });
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Paperclip ingestion policy denied")) {
    // read reason, un-archive space / set shared / add scopes, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/6c6da14e819eaeb1. Report an issue: GitHub.